diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-14 21:08:45 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-14 21:08:45 +0300 |
commit | 26a50872e9da9509c52c70f74dc21698fec906db (patch) | |
tree | b1bd36bd72e701e346ef880fc7a905f6186525e7 /app | |
parent | b3a736ed88a1db0391cd9881e70b987bab7d89d1 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
19 files changed, 248 insertions, 36 deletions
diff --git a/app/assets/javascripts/blob/components/blob_content.vue b/app/assets/javascripts/blob/components/blob_content.vue new file mode 100644 index 00000000000..2639a099093 --- /dev/null +++ b/app/assets/javascripts/blob/components/blob_content.vue @@ -0,0 +1,51 @@ +<script> +import { GlLoadingIcon } from '@gitlab/ui'; +import { RichViewer, SimpleViewer } from '~/vue_shared/components/blob_viewers'; +import BlobContentError from './blob_content_error.vue'; + +export default { + components: { + GlLoadingIcon, + BlobContentError, + }, + props: { + content: { + type: String, + default: '', + required: false, + }, + loading: { + type: Boolean, + default: true, + required: false, + }, + activeViewer: { + type: Object, + required: true, + }, + }, + computed: { + viewer() { + switch (this.activeViewer.type) { + case 'rich': + return RichViewer; + default: + return SimpleViewer; + } + }, + viewerError() { + return this.activeViewer.renderError; + }, + }, +}; +</script> +<template> + <div class="blob-viewer" :data-type="activeViewer.type"> + <gl-loading-icon v-if="loading" size="md" color="dark" class="my-4 mx-auto" /> + + <template v-else> + <blob-content-error v-if="viewerError" :viewer-error="viewerError" /> + <component :is="viewer" v-else ref="contentViewer" :content="content" /> + </template> + </div> +</template> diff --git a/app/assets/javascripts/blob/components/blob_content_error.vue b/app/assets/javascripts/blob/components/blob_content_error.vue new file mode 100644 index 00000000000..0f1af0a962d --- /dev/null +++ b/app/assets/javascripts/blob/components/blob_content_error.vue @@ -0,0 +1,15 @@ +<script> +export default { + props: { + viewerError: { + type: String, + required: true, + }, + }, +}; +</script> +<template> + <div class="file-content code"> + <div class="text-center py-4" v-html="viewerError"></div> + </div> +</template> diff --git a/app/assets/javascripts/blob/components/blob_header_default_actions.vue b/app/assets/javascripts/blob/components/blob_header_default_actions.vue index f5157fba819..6b38b871606 100644 --- a/app/assets/javascripts/blob/components/blob_header_default_actions.vue +++ b/app/assets/javascripts/blob/components/blob_header_default_actions.vue @@ -36,11 +36,6 @@ export default { return this.activeViewer === RICH_BLOB_VIEWER; }, }, - methods: { - requestCopyContents() { - this.$emit('copy'); - }, - }, BTN_COPY_CONTENTS_TITLE, BTN_DOWNLOAD_TITLE, BTN_RAW_TITLE, @@ -53,7 +48,7 @@ export default { :aria-label="$options.BTN_COPY_CONTENTS_TITLE" :title="$options.BTN_COPY_CONTENTS_TITLE" :disabled="copyDisabled" - @click="requestCopyContents" + data-clipboard-target="#blob-code-content" > <gl-icon name="copy-to-clipboard" :size="14" /> </gl-button> diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index 43f5e9954ce..6d2b11e39d3 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import Vue from 'vue'; import Cookies from 'js-cookie'; -import { GlEmptyState } from '@gitlab/ui'; +import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui'; import filterMixins from 'ee_else_ce/analytics/cycle_analytics/mixins/filter_mixins'; import Flash from '../flash'; import { __ } from '~/locale'; @@ -28,6 +28,7 @@ export default () => { name: 'CycleAnalytics', components: { GlEmptyState, + GlLoadingIcon, banner, 'stage-issue-component': stageComponent, 'stage-plan-component': stageComponent, diff --git a/app/assets/javascripts/graphql_shared/fragments/blobviewer.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/blobviewer.fragment.graphql index 64c894df115..b202ed12f80 100644 --- a/app/assets/javascripts/graphql_shared/fragments/blobviewer.fragment.graphql +++ b/app/assets/javascripts/graphql_shared/fragments/blobviewer.fragment.graphql @@ -1,6 +1,7 @@ fragment BlobViewer on SnippetBlobViewer { collapsed - loadingPartialName renderError tooLarge + type + fileType } diff --git a/app/assets/javascripts/snippets/components/snippet_blob_view.vue b/app/assets/javascripts/snippets/components/snippet_blob_view.vue index 49e0ef35cb8..4703a940e08 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_view.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_view.vue @@ -2,13 +2,19 @@ import BlobEmbeddable from '~/blob/components/blob_embeddable.vue'; import { SNIPPET_VISIBILITY_PUBLIC } from '../constants'; import BlobHeader from '~/blob/components/blob_header.vue'; -import GetSnippetBlobQuery from '../queries/snippet.blob.query.graphql'; +import BlobContent from '~/blob/components/blob_content.vue'; import { GlLoadingIcon } from '@gitlab/ui'; +import GetSnippetBlobQuery from '../queries/snippet.blob.query.graphql'; +import GetBlobContent from '../queries/snippet.blob.content.query.graphql'; + +import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constants'; + export default { components: { BlobEmbeddable, BlobHeader, + BlobContent, GlLoadingIcon, }, apollo: { @@ -20,6 +26,23 @@ export default { }; }, update: data => data.snippets.edges[0].node.blob, + result(res) { + const viewer = res.data.snippets.edges[0].node.blob.richViewer + ? RICH_BLOB_VIEWER + : SIMPLE_BLOB_VIEWER; + this.switchViewer(viewer, true); + }, + }, + blobContent: { + query: GetBlobContent, + variables() { + return { + ids: this.snippet.id, + rich: this.activeViewerType === RICH_BLOB_VIEWER, + }; + }, + update: data => + data.snippets.edges[0].node.blob.richData || data.snippets.edges[0].node.blob.plainData, }, }, props: { @@ -31,6 +54,8 @@ export default { data() { return { blob: {}, + blobContent: '', + activeViewerType: window.location.hash ? SIMPLE_BLOB_VIEWER : '', }; }, computed: { @@ -40,6 +65,18 @@ export default { isBlobLoading() { return this.$apollo.queries.blob.loading; }, + isContentLoading() { + return this.$apollo.queries.blobContent.loading; + }, + viewer() { + const { richViewer, simpleViewer } = this.blob; + return this.activeViewerType === RICH_BLOB_VIEWER ? richViewer : simpleViewer; + }, + }, + methods: { + switchViewer(newViewer, respectHash = false) { + this.activeViewerType = respectHash && window.location.hash ? SIMPLE_BLOB_VIEWER : newViewer; + }, }, }; </script> @@ -49,11 +86,12 @@ export default { <gl-loading-icon v-if="isBlobLoading" :label="__('Loading blob')" - :size="2" + size="lg" class="prepend-top-20 append-bottom-20" /> <article v-else class="file-holder snippet-file-content"> - <blob-header :blob="blob" /> + <blob-header :blob="blob" :active-viewer-type="viewer.type" @viewer-changed="switchViewer" /> + <blob-content :loading="isContentLoading" :content="blobContent" :active-viewer="viewer" /> </article> </div> </template> diff --git a/app/assets/javascripts/snippets/queries/snippet.blob.content.query.graphql b/app/assets/javascripts/snippets/queries/snippet.blob.content.query.graphql new file mode 100644 index 00000000000..889a88dd93c --- /dev/null +++ b/app/assets/javascripts/snippets/queries/snippet.blob.content.query.graphql @@ -0,0 +1,13 @@ +query SnippetBlobContent($ids: [ID!], $rich: Boolean!) { + snippets(ids: $ids) { + edges { + node { + id + blob { + richData @include(if: $rich) + plainData @skip(if: $rich) + } + } + } + } +} diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/constants.js b/app/assets/javascripts/vue_shared/components/blob_viewers/constants.js new file mode 100644 index 00000000000..d4c1808eec2 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/blob_viewers/constants.js @@ -0,0 +1,3 @@ +export const HIGHLIGHT_CLASS_NAME = 'hll'; + +export default {}; diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/index.js b/app/assets/javascripts/vue_shared/components/blob_viewers/index.js new file mode 100644 index 00000000000..72fba9392f9 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/blob_viewers/index.js @@ -0,0 +1,4 @@ +import RichViewer from './rich_viewer.vue'; +import SimpleViewer from './simple_viewer.vue'; + +export { RichViewer, SimpleViewer }; diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/mixins.js b/app/assets/javascripts/vue_shared/components/blob_viewers/mixins.js new file mode 100644 index 00000000000..582213ee8d3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/blob_viewers/mixins.js @@ -0,0 +1,8 @@ +export default { + props: { + content: { + type: String, + required: true, + }, + }, +}; diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue new file mode 100644 index 00000000000..b3a1df8f303 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue @@ -0,0 +1,10 @@ +<script> +import ViewerMixin from './mixins'; + +export default { + mixins: [ViewerMixin], +}; +</script> +<template> + <div v-html="content"></div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue new file mode 100644 index 00000000000..e64c7132117 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue @@ -0,0 +1,68 @@ +<script> +import ViewerMixin from './mixins'; +import { GlIcon } from '@gitlab/ui'; +import { HIGHLIGHT_CLASS_NAME } from './constants'; + +export default { + components: { + GlIcon, + }, + mixins: [ViewerMixin], + data() { + return { + highlightedLine: null, + }; + }, + computed: { + lineNumbers() { + return this.content.split('\n').length; + }, + }, + mounted() { + const { hash } = window.location; + if (hash) this.scrollToLine(hash, true); + }, + methods: { + scrollToLine(hash, scroll = false) { + const lineToHighlight = hash && this.$el.querySelector(hash); + const currentlyHighlighted = this.highlightedLine; + if (lineToHighlight) { + if (currentlyHighlighted) { + currentlyHighlighted.classList.remove(HIGHLIGHT_CLASS_NAME); + } + + lineToHighlight.classList.add(HIGHLIGHT_CLASS_NAME); + this.highlightedLine = lineToHighlight; + if (scroll) { + lineToHighlight.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + } + }, + }, + userColorScheme: window.gon.user_color_scheme, +}; +</script> +<template> + <div + class="file-content code js-syntax-highlight qa-file-content" + :class="$options.userColorScheme" + > + <div class="line-numbers"> + <a + v-for="line in lineNumbers" + :id="`L${line}`" + :key="line" + class="diff-line-num js-line-number" + :href="`#LC${line}`" + :data-line-number="line" + @click="scrollToLine(`#LC${line}`)" + > + <gl-icon :size="12" name="link" /> + {{ line }} + </a> + </div> + <div class="blob-content"> + <pre class="code highlight"><code id="blob-code-content" v-html="content"></code></pre> + </div> + </div> +</template> diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index ee6e53adaf7..73a2170fc68 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -30,7 +30,6 @@ .line { display: block; width: 100%; - min-height: 1.5em; padding-left: 10px; padding-right: 10px; white-space: pre; @@ -48,10 +47,10 @@ font-family: $monospace-font; display: block; font-size: $code-font-size !important; - min-height: 1.5em; white-space: nowrap; - i { + i, + svg { float: left; margin-top: 3px; margin-right: 5px; @@ -62,12 +61,20 @@ &:focus { outline: none; - i { + i, + svg { visibility: visible; } } } } + + pre .line, + .line-numbers a { + font-size: 0.8125rem; + line-height: 1.1875rem; + min-height: 1.1875rem; + } } // Vertically aligns <table> line numbers (eg. blame view) diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index 76cd4f34865..21b23feb57f 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -109,14 +109,6 @@ top: $gl-padding-top; } - .fa-spinner { - font-size: 28px; - position: relative; - margin-left: -20px; - left: 50%; - margin-top: 36px; - } - .stage-panel-body { display: flex; flex-wrap: wrap; diff --git a/app/controllers/concerns/metrics_dashboard.rb b/app/controllers/concerns/metrics_dashboard.rb index dc392147cb8..fa79f3bc4e6 100644 --- a/app/controllers/concerns/metrics_dashboard.rb +++ b/app/controllers/concerns/metrics_dashboard.rb @@ -5,6 +5,7 @@ module MetricsDashboard include RenderServiceResults include ChecksCollaboration + include EnvironmentsHelper extend ActiveSupport::Concern @@ -15,8 +16,9 @@ module MetricsDashboard metrics_dashboard_params.to_h.symbolize_keys ) - if include_all_dashboards? && result - result[:all_dashboards] = all_dashboards + if result + result[:all_dashboards] = all_dashboards if include_all_dashboards? + result[:metrics_data] = metrics_data(project_for_dashboard, environment_for_dashboard) if project_for_dashboard && environment_for_dashboard end respond_to do |format| @@ -76,10 +78,14 @@ module MetricsDashboard defined?(project) ? project : nil end + def environment_for_dashboard + defined?(environment) ? environment : nil + end + def dashboard_success_response(result) { status: :ok, - json: result.slice(:all_dashboards, :dashboard, :status) + json: result.slice(:all_dashboards, :dashboard, :status, :metrics_data) } end diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 579a57a27de..fd330d4efd9 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true module EnvironmentsHelper + include ActionView::Helpers::AssetUrlHelper prepend_if_ee('::EE::EnvironmentsHelper') # rubocop: disable Cop/InjectEnterpriseEditionModule def environments_list_data @@ -21,7 +22,7 @@ module EnvironmentsHelper { "settings-path" => edit_project_service_path(project, 'prometheus'), "clusters-path" => project_clusters_path(project), - "current-environment-name": environment.name, + "current-environment-name" => environment.name, "documentation-path" => help_page_path('administration/monitoring/prometheus/index.md'), "empty-getting-started-svg-path" => image_path('illustrations/monitoring/getting_started.svg'), "empty-loading-svg-path" => image_path('illustrations/monitoring/loading.svg'), diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index 70409774a50..b0d9dfb0d37 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -5,7 +5,7 @@ %banner{ "v-if" => "!isOverviewDialogDismissed", "documentation-link": help_page_path('user/analytics/value_stream_analytics.md'), "v-on:dismiss-overview-dialog" => "dismissOverviewDialog()" } - = icon("spinner spin", "v-show" => "isLoading") + %gl-loading-icon{ "v-show" => "isLoading", "size" => "lg" } .wrapper{ "v-show" => "!isLoading && !hasError" } .card .card-header @@ -57,8 +57,7 @@ %ul %stage-nav-item{ "v-for" => "stage in state.stages", ":key" => '`ca-stage-title-${stage.title}`', '@select' => 'selectStage(stage)', ":title" => "stage.title", ":is-user-allowed" => "stage.isUserAllowed", ":value" => "stage.value", ":is-active" => "stage.active" } .section.stage-events - %template{ "v-if" => "isLoadingStage" } - = icon("spinner spin") + %gl-loading-icon{ "v-show" => "isLoadingStage", "size" => "lg" } %template{ "v-if" => "currentStage && !currentStage.isUserAllowed" } = render partial: "no_access" %template{ "v-else" => true } diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 768e4422206..422a467574b 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -12,7 +12,7 @@ %article.file-holder.snippet-file-content = render 'shared/snippets/blob' - .row-content-block.top-block.content-component-block - = render 'award_emoji/awards_block', awardable: @snippet, inline: true +.row-content-block.top-block.content-component-block + = render 'award_emoji/awards_block', awardable: @snippet, inline: true - #notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => true +#notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => true diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 080c0ab6ece..30f760f2122 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -13,7 +13,7 @@ %article.file-holder.snippet-file-content = render 'shared/snippets/blob' - .row-content-block.top-block.content-component-block - = render 'award_emoji/awards_block', awardable: @snippet, inline: true +.row-content-block.top-block.content-component-block + = render 'award_emoji/awards_block', awardable: @snippet, inline: true - #notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => false +#notes.limited-width-notes= render "shared/notes/notes_with_form", :autocomplete => false |