diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-25 00:09:08 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-25 00:09:08 +0300 |
commit | 7671216b60e2796a050358ff808b4a0c2de3d22f (patch) | |
tree | 605dfc1339a3cd7dc7353ac6d725191086a9acca /app/assets | |
parent | c2367afbf57ebc65d5b78a743b5d6a91f0aece9f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
8 files changed, 300 insertions, 20 deletions
diff --git a/app/assets/javascripts/blob/pipeline_tour_success_modal.vue b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue new file mode 100644 index 00000000000..0739b4d5e39 --- /dev/null +++ b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue @@ -0,0 +1,78 @@ +<script> +import { GlModal, GlSprintf, GlLink } from '@gitlab/ui'; +import { sprintf, s__, __ } from '~/locale'; +import Cookies from 'js-cookie'; +import { glEmojiTag } from '~/emoji'; + +export default { + beginnerLink: + 'https://about.gitlab.com/blog/2018/01/22/a-beginners-guide-to-continuous-integration/', + exampleLink: 'https://docs.gitlab.com/ee/ci/examples/', + bodyMessage: s__( + 'MR widget|The pipeline will now run automatically every time you commit code. Pipelines are useful for deploying static web pages, detecting vulnerabilities in dependencies, static or dynamic application security testing (SAST and DAST), and so much more!', + ), + modalTitle: sprintf( + __("That's it, well done!%{celebrate}"), + { + celebrate: glEmojiTag('tada'), + }, + false, + ), + components: { + GlModal, + GlSprintf, + GlLink, + }, + props: { + goToPipelinesPath: { + type: String, + required: true, + }, + commitCookie: { + type: String, + required: true, + }, + }, + mounted() { + this.disableModalFromRenderingAgain(); + }, + methods: { + disableModalFromRenderingAgain() { + Cookies.remove(this.commitCookie); + }, + }, +}; +</script> +<template> + <gl-modal + visible + size="sm" + :title="$options.modalTitle" + modal-id="success-pipeline-modal-id-not-used" + > + <p> + {{ $options.bodyMessage }} + </p> + <gl-sprintf + :message=" + s__(`MR widget|Take a look at our %{beginnerLinkStart}Beginner's Guide to Continuous Integration%{beginnerLinkEnd} + and our %{exampleLinkStart}examples of GitLab CI/CD%{exampleLinkEnd} + to see all the cool stuff you can do with it.`) + " + > + <template #beginnerLink="{content}"> + <gl-link :href="$options.beginnerLink" target="_blank"> + {{ content }} + </gl-link> + </template> + <template #exampleLink="{content}"> + <gl-link :href="$options.exampleLink" target="_blank"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + <template #modal-footer> + <a :href="goToPipelinesPath" class="btn btn-success">{{ __('Go to Pipelines') }}</a> + </template> + </gl-modal> +</template> diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index caf9a8c0b64..4d308d6b07a 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -4,6 +4,7 @@ import BlobViewer from '~/blob/viewer/index'; import initBlob from '~/pages/projects/init_blob'; import GpgBadges from '~/gpg_badges'; import '~/sourcegraph/load'; +import PipelineTourSuccessModal from '~/blob/pipeline_tour_success_modal.vue'; document.addEventListener('DOMContentLoaded', () => { new BlobViewer(); // eslint-disable-line no-new @@ -35,4 +36,25 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line promise/catch-or-return import('~/code_navigation').then(m => m.default()); } + + if (gon.features?.suggestPipeline) { + const successPipelineEl = document.querySelector('.js-success-pipeline-modal'); + + if (successPipelineEl) { + // eslint-disable-next-line no-new + new Vue({ + el: successPipelineEl, + render(createElement) { + const { commitCookie, pipelinesPath: goToPipelinesPath } = this.$el.dataset; + + return createElement(PipelineTourSuccessModal, { + props: { + goToPipelinesPath, + commitCookie, + }, + }); + }, + }); + } + } }); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue index f08bfb3a90f..9942861d9e4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue @@ -1,45 +1,75 @@ <script> import { GlLink, GlSprintf } from '@gitlab/ui'; import MrWidgetIcon from './mr_widget_icon.vue'; +import PipelineTourState from './states/mr_widget_pipeline_tour.vue'; export default { name: 'MRWidgetSuggestPipeline', iconName: 'status_notfound', + popoverTarget: 'suggest-popover', + popoverContainer: 'suggest-pipeline', + trackLabel: 'no_pipeline_noticed', + linkTrackValue: 30, + linkTrackEvent: 'click_link', components: { GlLink, GlSprintf, MrWidgetIcon, + PipelineTourState, }, props: { pipelinePath: { type: String, required: true, }, + pipelineSvgPath: { + type: String, + required: true, + }, + humanAccess: { + type: String, + required: true, + }, }, }; </script> <template> - <div class="d-flex mr-pipeline-suggest append-bottom-default"> + <div :id="$options.popoverContainer" class="d-flex mr-pipeline-suggest append-bottom-default"> <mr-widget-icon :name="$options.iconName" /> - <gl-sprintf - class="js-no-pipeline-message" - :message=" - s__(`mrWidget|%{prefixToLinkStart}No pipeline%{prefixToLinkEnd} + <div :id="$options.popoverTarget"> + <gl-sprintf + :message=" + s__(`mrWidget|%{prefixToLinkStart}No pipeline%{prefixToLinkEnd} %{addPipelineLinkStart}Add the .gitlab-ci.yml file%{addPipelineLinkEnd} to create one.`) - " - > - <template #prefixToLink="{content}"> - <strong> - {{ content }} - </strong> - </template> - <template #addPipelineLink="{content}"> - <gl-link :href="pipelinePath" class="ml-2"> - {{ content }} - </gl-link> - - </template> - </gl-sprintf> + " + > + <template #prefixToLink="{content}"> + <strong> + {{ content }} + </strong> + </template> + <template #addPipelineLink="{content}"> + <gl-link + :href="pipelinePath" + class="ml-2 js-add-pipeline-path" + :data-track-property="humanAccess" + :data-track-value="$options.linkTrackValue" + :data-track-event="$options.linkTrackEvent" + :data-track-label="$options.trackLabel" + > + {{ content }} + </gl-link> + </template> + </gl-sprintf> + <pipeline-tour-state + :pipeline-path="pipelinePath" + :pipeline-svg-path="pipelineSvgPath" + :human-access="humanAccess" + :popover-target="$options.popoverTarget" + :popover-container="$options.popoverContainer" + :track-label="$options.trackLabel" + /> + </div> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue new file mode 100644 index 00000000000..f2d7e86a85e --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue @@ -0,0 +1,143 @@ +<script> +import { s__, sprintf } from '~/locale'; +import { GlPopover, GlButton } from '@gitlab/ui'; +import Icon from '~/vue_shared/components/icon.vue'; +import Cookies from 'js-cookie'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import Tracking from '~/tracking'; + +const trackingMixin = Tracking.mixin(); + +const cookieKey = 'suggest_pipeline_dismissed'; + +export default { + name: 'MRWidgetPipelineTour', + dismissTrackValue: 20, + showTrackValue: 10, + trackEvent: 'click_button', + popoverContent: sprintf( + '%{messageText1}%{lineBreak}%{messageText2}%{lineBreak}%{messageText3}%{lineBreak}%{messageText4}%{lineBreak}%{messageText5}', + { + messageText1: s__('mrWidget|Detect issues before deployment with a CI pipeline'), + messageText2: s__('mrWidget|that continuously tests your code. We created'), + messageText3: s__("mrWidget|a quick guide that'll show you how to create"), + messageText4: s__('mrWidget|one. Make your code more secure and more'), + messageText5: s__('mrWidget|robust in just a minute.'), + lineBreak: '<br/>', + }, + false, + ), + components: { + GlPopover, + GlButton, + Icon, + }, + mixins: [trackingMixin], + props: { + pipelinePath: { + type: String, + required: true, + }, + pipelineSvgPath: { + type: String, + required: true, + }, + humanAccess: { + type: String, + required: true, + }, + popoverTarget: { + type: String, + required: true, + }, + popoverContainer: { + type: String, + required: true, + }, + trackLabel: { + type: String, + required: true, + }, + }, + data() { + return { + popoverDismissed: parseBoolean(Cookies.get(cookieKey)), + tracking: { + label: this.trackLabel, + property: this.humanAccess, + }, + }; + }, + mounted() { + this.trackOnShow(); + }, + methods: { + trackOnShow() { + if (!this.popoverDismissed) { + this.track(); + } + }, + dismissPopover() { + this.popoverDismissed = true; + Cookies.set(cookieKey, this.popoverDismissed, { expires: 365 }); + }, + }, +}; +</script> +<template> + <gl-popover + v-if="!popoverDismissed" + show + :target="popoverTarget" + :container="popoverContainer" + placement="rightbottom" + > + <template #title> + <button + class="btn-blank float-right mt-1" + type="button" + :aria-label="__('Close')" + :data-track-property="humanAccess" + :data-track-value="$options.dismissTrackValue" + :data-track-event="$options.trackEvent" + :data-track-label="trackLabel" + @click="dismissPopover" + > + <icon name="close" aria-hidden="true" /> + </button> + {{ s__('mrWidget|Are you adding technical debt or code vulnerabilities?') }} + </template> + <div class="svg-content svg-150 pt-1"> + <img :src="pipelineSvgPath" /> + </div> + <p v-html="$options.popoverContent"></p> + <gl-button + ref="ok" + category="primary" + class="mt-2 mb-0" + variant="info" + block + :href="pipelinePath" + :data-track-property="humanAccess" + :data-track-value="$options.showTrackValue" + :data-track-event="$options.trackEvent" + :data-track-label="trackLabel" + > + {{ __('Show me how') }} + </gl-button> + <gl-button + ref="no-thanks" + category="secondary" + class="mt-2 mb-0" + variant="info" + block + :data-track-property="humanAccess" + :data-track-value="$options.dismissTrackValue" + :data-track-event="$options.trackEvent" + :data-track-label="trackLabel" + @click="dismissPopover" + > + {{ __("No thanks, don't show this again") }} + </gl-button> + </gl-popover> +</template> 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 27f13ace779..c8d69143f8d 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 @@ -362,6 +362,8 @@ export default { v-if="shouldSuggestPipelines" class="mr-widget-workflow" :pipeline-path="mr.mergeRequestAddCiConfigPath" + :pipeline-svg-path="mr.pipelinesEmptySvgPath" + :human-access="mr.humanAccess.toLowerCase()" /> <mr-widget-pipeline-container v-if="shouldRenderPipelines" diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 73a0b3cb673..ea83c61e275 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -176,6 +176,7 @@ export default class MergeRequestStore { this.eligibleApproversDocsPath = data.eligible_approvers_docs_path; this.mergeImmediatelyDocsPath = data.merge_immediately_docs_path; this.mergeRequestAddCiConfigPath = data.merge_request_add_ci_config_path; + this.pipelinesEmptySvgPath = data.pipelines_empty_svg_path; this.humanAccess = data.human_access; } diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index d78c707192f..2c9397d363c 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -20,7 +20,7 @@ width: 100%; } - $image-widths: 80 130 250 306 394 430; + $image-widths: 80 130 150 250 306 394 430; @each $width in $image-widths { &.svg-#{$width} { img, diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 5ca75c28ac3..ad8b251d3e4 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -614,6 +614,10 @@ $mr-widget-min-height: 69px; .circle-icon-container { color: $gl-text-color-quaternary; } + + .popover { + z-index: 240; + } } .card-new-merge-request { |