diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /app/assets/javascripts/pipelines/components/pipeline_graph | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) | |
download | gitlab-ce-85dc423f7090da0a52c73eb66faf22ddb20efff9.tar.gz |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'app/assets/javascripts/pipelines/components/pipeline_graph')
4 files changed, 192 insertions, 0 deletions
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue new file mode 100644 index 00000000000..3cc76425e2a --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue @@ -0,0 +1,76 @@ +<script> +import { GlTab, GlTabs } from '@gitlab/ui'; +import jsYaml from 'js-yaml'; +import PipelineGraph from './pipeline_graph.vue'; +import { preparePipelineGraphData } from '../../utils'; + +export default { + FILE_CONTENT_SELECTOR: '#blob-content', + EMPTY_FILE_SELECTOR: '.nothing-here-block', + + components: { + GlTab, + GlTabs, + PipelineGraph, + }, + props: { + blobData: { + required: true, + type: String, + }, + }, + data() { + return { + selectedTabIndex: 0, + pipelineData: {}, + }; + }, + computed: { + isVisualizationTab() { + return this.selectedTabIndex === 1; + }, + }, + async created() { + if (this.blobData) { + // The blobData in this case represents the gitlab-ci.yml data + const json = await jsYaml.load(this.blobData); + this.pipelineData = preparePipelineGraphData(json); + } + }, + methods: { + // This is used because the blob page still uses haml, and we can't make + // our haml hide the unused section so we resort to a standard query here. + toggleFileContent({ isFileTab }) { + const el = document.querySelector(this.$options.FILE_CONTENT_SELECTOR); + const emptySection = document.querySelector(this.$options.EMPTY_FILE_SELECTOR); + + const elementToHide = el || emptySection; + + if (!elementToHide) { + return; + } + + // Checking for the current style display prevents user + // from toggling visiblity on and off when clicking on the tab + if (!isFileTab && elementToHide.style.display !== 'none') { + elementToHide.style.display = 'none'; + } + + if (isFileTab && elementToHide.style.display === 'none') { + elementToHide.style.display = 'block'; + } + }, + }, +}; +</script> +<template> + <div> + <div> + <gl-tabs v-model="selectedTabIndex"> + <gl-tab :title="__('File')" @click="toggleFileContent({ isFileTab: true })" /> + <gl-tab :title="__('Visualization')" @click="toggleFileContent({ isFileTab: false })" /> + </gl-tabs> + </div> + <pipeline-graph v-if="isVisualizationTab" :pipeline-data="pipelineData" /> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue new file mode 100644 index 00000000000..19d41b166c3 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue @@ -0,0 +1,24 @@ +<script> +import tooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; + +export default { + components: { + tooltipOnTruncate, + }, + props: { + jobName: { + type: String, + required: true, + }, + }, +}; +</script> +<template> + <tooltip-on-truncate :title="jobName" truncate-target="child" placement="top"> + <div + class="gl-bg-white gl-text-center gl-text-truncate gl-rounded-pill gl-inset-border-1-green-600 gl-mb-3 gl-px-5 gl-py-2 pipeline-job-pill " + > + {{ jobName }} + </div> + </tooltip-on-truncate> +</template> diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue new file mode 100644 index 00000000000..6a0d3cce1f3 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue @@ -0,0 +1,57 @@ +<script> +import { isEmpty } from 'lodash'; +import { GlAlert } from '@gitlab/ui'; +import JobPill from './job_pill.vue'; +import StagePill from './stage_pill.vue'; + +export default { + components: { + GlAlert, + JobPill, + StagePill, + }, + props: { + pipelineData: { + required: true, + type: Object, + }, + }, + computed: { + isPipelineDataEmpty() { + return isEmpty(this.pipelineData); + }, + emptyClass() { + return !this.isPipelineDataEmpty ? 'gl-py-7' : ''; + }, + }, +}; +</script> +<template> + <div class="gl-display-flex gl-bg-gray-50 gl-px-4 gl-overflow-auto" :class="emptyClass"> + <gl-alert v-if="isPipelineDataEmpty" variant="tip" :dismissible="false"> + {{ __('No content to show') }} + </gl-alert> + <template v-else> + <div + v-for="(stage, index) in pipelineData.stages" + :key="`${stage.name}-${index}`" + class="gl-flex-direction-column" + > + <div + class="gl-display-flex gl-align-items-center gl-bg-white gl-w-full gl-px-8 gl-py-4 gl-mb-5" + :class="{ + 'stage-left-rounded': index === 0, + 'stage-right-rounded': index === pipelineData.stages.length - 1, + }" + > + <stage-pill :stage-name="stage.name" :is-empty="stage.groups.length === 0" /> + </div> + <div + class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-w-full gl-px-8" + > + <job-pill v-for="group in stage.groups" :key="group.name" :job-name="group.name" /> + </div> + </div> + </template> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/stage_pill.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_pill.vue new file mode 100644 index 00000000000..7b2458db725 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_pill.vue @@ -0,0 +1,35 @@ +<script> +import tooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; + +export default { + components: { + tooltipOnTruncate, + }, + props: { + stageName: { + type: String, + required: true, + }, + isEmpty: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + emptyClass() { + return this.isEmpty ? 'gl-bg-gray-200' : 'gl-bg-gray-600'; + }, + }, +}; +</script> +<template> + <tooltip-on-truncate :title="stageName" truncate-target="child" placement="top"> + <div + class="gl-px-5 gl-py-2 gl-text-white gl-text-center gl-text-truncate gl-rounded-pill pipeline-stage-pill" + :class="emptyClass" + > + {{ stageName }} + </div> + </tooltip-on-truncate> +</template> |