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:
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget/components/widget')
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue98
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue67
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue49
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue68
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue35
5 files changed, 264 insertions, 53 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue
new file mode 100644
index 00000000000..d1ade2886f4
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/dynamic_content.vue
@@ -0,0 +1,98 @@
+<script>
+import { GlBadge, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
+import Actions from '../action_buttons.vue';
+import { generateText } from '../extensions/utils';
+import ContentRow from './widget_content_row.vue';
+
+export default {
+ name: 'DynamicContent',
+ components: {
+ GlBadge,
+ GlLink,
+ Actions,
+ ContentRow,
+ },
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ props: {
+ data: {
+ type: Object,
+ required: true,
+ },
+ widgetName: {
+ type: String,
+ required: true,
+ },
+ level: {
+ type: Number,
+ required: false,
+ default: 2,
+ },
+ },
+ computed: {
+ statusIcon() {
+ return this.data.icon?.name || undefined;
+ },
+ generatedText() {
+ return generateText(this.data.text);
+ },
+ generatedSubtext() {
+ return generateText(this.data.subtext);
+ },
+ generatedSupportingText() {
+ return generateText(this.data.supportingText);
+ },
+ },
+ methods: {
+ onClickedAction(action) {
+ this.$emit('clickedAction', action);
+ },
+ },
+};
+</script>
+
+<template>
+ <content-row
+ :level="level"
+ :status-icon-name="statusIcon"
+ :widget-name="widgetName"
+ :header="data.header"
+ >
+ <template #body>
+ <div class="gl-display-flex gl-flex-direction-column">
+ <div>
+ <p v-safe-html="generatedText" class="gl-mb-0"></p>
+ <gl-link v-if="data.link" :href="data.link.href">{{ data.link.text }}</gl-link>
+ <p v-if="data.supportingText" v-safe-html="generatedSupportingText" class="gl-mb-0"></p>
+ <gl-badge v-if="data.badge" :variant="data.badge.variant || 'info'">
+ {{ data.badge.text }}
+ </gl-badge>
+ <actions
+ :widget="widgetName"
+ :tertiary-buttons="data.actions"
+ class="gl-ml-auto gl-pl-3"
+ @clickedAction="onClickedAction"
+ />
+ <p v-if="data.subtext" v-safe-html="generatedSubtext" class="gl-m-0 gl-font-sm"></p>
+ </div>
+ <ul
+ v-if="data.children && data.children.length > 0 && level === 2"
+ class="gl-m-0 gl-p-0 gl-list-style-none"
+ >
+ <li>
+ <dynamic-content
+ v-for="(childData, index) in data.children"
+ :key="childData.id || index"
+ :data="childData"
+ :widget-name="widgetName"
+ :level="3"
+ data-qa-selector="child_content"
+ @clickedAction="onClickedAction"
+ />
+ </li>
+ </ul>
+ </div>
+ </template>
+ </content-row>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue
new file mode 100644
index 00000000000..ff17de343d6
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/status_icon.vue
@@ -0,0 +1,67 @@
+<script>
+import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { EXTENSION_ICON_CLASS, EXTENSION_ICON_NAMES } from '../../constants';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ GlIcon,
+ },
+ props: {
+ level: {
+ type: Number,
+ required: false,
+ default: 1,
+ },
+ name: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ iconName: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ iconAriaLabel() {
+ return `${capitalizeFirstCharacter(this.iconName)} ${this.name}`;
+ },
+ iconSize() {
+ return this.level === 1 ? 16 : 12;
+ },
+ },
+ EXTENSION_ICON_NAMES,
+ EXTENSION_ICON_CLASS,
+};
+</script>
+
+<template>
+ <div :class="[$options.EXTENSION_ICON_CLASS[iconName]]" class="gl-mr-3">
+ <gl-loading-icon v-if="isLoading" size="md" inline />
+ <div
+ v-else
+ class="gl-display-flex gl-align-items-center gl-justify-content-center gl-rounded-full gl-bg-gray-10"
+ :class="{
+ 'gl-p-2': level === 1,
+ }"
+ >
+ <div class="gl-rounded-full gl-bg-white">
+ <gl-icon
+ :name="$options.EXTENSION_ICON_NAMES[iconName]"
+ :size="iconSize"
+ :aria-label="iconAriaLabel"
+ :data-qa-selector="`status_${iconName}_icon`"
+ class="gl-display-block"
+ />
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
index c9fc2dde0bd..94359d7d6ac 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
@@ -4,10 +4,11 @@ import * as Sentry from '@sentry/browser';
import { normalizeHeaders } from '~/lib/utils/common_utils';
import { sprintf, __ } from '~/locale';
import Poll from '~/lib/utils/poll';
-import StatusIcon from '../extensions/status_icon.vue';
import ActionButtons from '../action_buttons.vue';
import { EXTENSION_ICONS } from '../../constants';
-import ContentSection from './widget_content_section.vue';
+import ContentRow from './widget_content_row.vue';
+import DynamicContent from './dynamic_content.vue';
+import StatusIcon from './status_icon.vue';
const FETCH_TYPE_COLLAPSED = 'collapsed';
const FETCH_TYPE_EXPANDED = 'expanded';
@@ -18,7 +19,8 @@ export default {
StatusIcon,
GlButton,
GlLoadingIcon,
- ContentSection,
+ ContentRow,
+ DynamicContent,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -59,7 +61,7 @@ export default {
},
// If the content slot is not used, this value will be used as a fallback.
content: {
- type: Object,
+ type: Array,
required: false,
default: undefined,
},
@@ -187,7 +189,7 @@ export default {
<template>
<section class="media-section" data-testid="widget-extension">
- <div class="media gl-p-5">
+ <div class="gl-p-5 gl-align-items-center gl-display-flex">
<status-icon
:level="1"
:name="widgetName"
@@ -227,23 +229,34 @@ export default {
</div>
<div
v-if="!isCollapsed || contentError"
- class="mr-widget-grouped-section gl-relative"
+ class="gl-relative gl-bg-gray-10"
data-testid="widget-extension-collapsed-section"
>
<div v-if="isLoadingExpandedContent" class="report-block-container gl-text-center">
- <gl-loading-icon size="sm" inline /> {{ __('Loading...') }}
+ <gl-loading-icon size="sm" inline /> {{ loadingText }}
+ </div>
+ <div v-else class="gl-px-5 gl-display-flex">
+ <content-row
+ v-if="contentError"
+ :level="2"
+ :status-icon-name="$options.failedStatusIcon"
+ :widget-name="widgetName"
+ >
+ <template #body>
+ {{ contentError }}
+ </template>
+ </content-row>
+ <div v-else class="gl-w-full">
+ <slot name="content">
+ <dynamic-content
+ v-for="(data, index) in content"
+ :key="data.id || index"
+ :data="data"
+ :widget-name="widgetName"
+ />
+ </slot>
+ </div>
</div>
- <content-section
- v-else-if="contentError"
- class="report-block-container"
- :status-icon-name="$options.failedStatusIcon"
- :widget-name="widgetName"
- >
- {{ contentError }}
- </content-section>
- <slot v-else name="content">
- {{ content }}
- </slot>
</div>
</section>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue
new file mode 100644
index 00000000000..ee81f0950a8
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_row.vue
@@ -0,0 +1,68 @@
+<script>
+import { GlSafeHtmlDirective } from '@gitlab/ui';
+import { EXTENSION_ICONS } from '../../constants';
+import { generateText } from '../extensions/utils';
+import StatusIcon from './status_icon.vue';
+
+export default {
+ components: {
+ StatusIcon,
+ },
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ props: {
+ level: {
+ type: Number,
+ required: true,
+ validator: (value) => value === 2 || value === 3,
+ },
+ statusIconName: {
+ type: String,
+ default: '',
+ required: false,
+ validator: (value) => value === '' || Object.keys(EXTENSION_ICONS).includes(value),
+ },
+ widgetName: {
+ type: String,
+ required: true,
+ },
+ header: {
+ type: [String, Array],
+ default: '',
+ required: false,
+ },
+ },
+ computed: {
+ generatedHeader() {
+ return generateText(Array.isArray(this.header) ? this.header[0] : this.header);
+ },
+ generatedSubheader() {
+ return Array.isArray(this.header) && this.header[1] ? generateText(this.header[1]) : '';
+ },
+ },
+};
+</script>
+<template>
+ <div
+ class="gl-w-full gl-display-flex mr-widget-content-row gl-align-items-baseline"
+ :class="{ 'gl-border-t gl-py-3 gl-pl-7': level === 2 }"
+ >
+ <status-icon v-if="statusIconName" :level="2" :name="widgetName" :icon-name="statusIconName" />
+ <div>
+ <slot name="header">
+ <div v-if="header" class="gl-mb-2">
+ <strong v-safe-html="generatedHeader" class="gl-display-block"></strong
+ ><span
+ v-if="generatedSubheader"
+ v-safe-html="generatedSubheader"
+ class="gl-display-block"
+ ></span>
+ </div>
+ </slot>
+ <div class="gl-display-flex gl-align-items-baseline gl-w-full">
+ <slot name="body"></slot>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue
deleted file mode 100644
index 61e3744b5dc..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-<script>
-import { EXTENSION_ICONS } from '../../constants';
-import StatusIcon from '../extensions/status_icon.vue';
-
-export default {
- components: {
- StatusIcon,
- },
- props: {
- statusIconName: {
- type: String,
- default: '',
- required: false,
- validator: (value) => value === '' || Object.keys(EXTENSION_ICONS).includes(value),
- },
- widgetName: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-<template>
- <div class="gl-px-7">
- <div class="gl-pl-4 gl-display-flex">
- <status-icon
- v-if="statusIconName"
- :level="2"
- :name="widgetName"
- :icon-name="statusIconName"
- />
- <slot name="default"></slot>
- </div>
- </div>
-</template>