diff options
Diffstat (limited to 'app')
21 files changed, 228 insertions, 160 deletions
diff --git a/app/assets/javascripts/batch_comments/components/diff_file_drafts.vue b/app/assets/javascripts/batch_comments/components/diff_file_drafts.vue index 570954c7200..2ebde10c229 100644 --- a/app/assets/javascripts/batch_comments/components/diff_file_drafts.vue +++ b/app/assets/javascripts/batch_comments/components/diff_file_drafts.vue @@ -1,11 +1,13 @@ <script> import { mapGetters } from 'vuex'; import imageDiff from '~/diffs/mixins/image_diff'; +import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue'; import DraftNote from './draft_note.vue'; export default { components: { DraftNote, + DesignNotePin, }, mixins: [imageDiff], props: { @@ -31,9 +33,12 @@ export default { class="discussion-notes diff-discussions position-relative" > <div class="notes"> - <span class="d-block btn-transparent badge badge-pill is-draft js-diff-notes-index"> - {{ toggleText(draft, index) }} - </span> + <design-note-pin + :label="toggleText(draft, index)" + is-draft + class="js-diff-notes-index gl-translate-x-n50" + size="sm" + /> <draft-note :draft="draft" /> </div> </div> diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js index ab57cbbc7c0..82bbbe891e2 100644 --- a/app/assets/javascripts/deprecated_notes.js +++ b/app/assets/javascripts/deprecated_notes.js @@ -694,7 +694,7 @@ export default class Notes { // Convert returned HTML to a jQuery object so we can modify it further const $noteEntityEl = $(noteEntity.html); const $noteAvatar = $noteEntityEl.find('.image-diff-avatar-link'); - const $targetNoteBadge = $targetNote.find('.badge'); + const $targetNoteBadge = $targetNote.find('.design-note-pin'); $noteAvatar.append($targetNoteBadge); this.revertNoteEditForm($targetNote); diff --git a/app/assets/javascripts/design_management/components/design_overlay.vue b/app/assets/javascripts/design_management/components/design_overlay.vue index b058709b316..674415ec449 100644 --- a/app/assets/javascripts/design_management/components/design_overlay.vue +++ b/app/assets/javascripts/design_management/components/design_overlay.vue @@ -286,6 +286,7 @@ export default { " :is-inactive="isNoteInactive(note)" :is-resolved="note.resolved" + is-on-image @mousedown.stop="onNoteMousedown($event, note)" @mouseup.stop="onNoteMouseup(note)" /> diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue index 5e05ec87f84..47a05ce11cc 100644 --- a/app/assets/javascripts/diffs/components/diff_discussions.vue +++ b/app/assets/javascripts/diffs/components/diff_discussions.vue @@ -1,12 +1,14 @@ <script> import { GlIcon } from '@gitlab/ui'; import { mapActions } from 'vuex'; +import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue'; import noteableDiscussion from '../../notes/components/noteable_discussion.vue'; export default { components: { noteableDiscussion, GlIcon, + DesignNotePin, }, props: { discussions: { @@ -62,20 +64,22 @@ export default { <ul :data-discussion-id="discussion.id" class="notes"> <template v-if="shouldCollapseDiscussions"> <button - :class="{ - 'diff-notes-collapse': discussion.expanded, - 'btn-transparent badge badge-pill': !discussion.expanded, - }" + v-if="discussion.expanded" + class="diff-notes-collapse js-diff-notes-toggle" type="button" - class="js-diff-notes-toggle" :aria-label="__('Show comments')" @click="toggleDiscussion({ discussionId: discussion.id })" > - <gl-icon v-if="discussion.expanded" name="collapse" class="collapse-icon" /> - <template v-else> - {{ index + 1 }} - </template> + <gl-icon name="collapse" class="collapse-icon" /> </button> + <design-note-pin + v-else + :label="index + 1" + :is-resolved="discussion.resolved" + size="sm" + class="js-diff-notes-toggle gl-translate-x-n50" + @click="toggleDiscussion({ discussionId: discussion.id })" + /> </template> <noteable-discussion v-show="isExpanded(discussion)" @@ -87,9 +91,12 @@ export default { @noteDeleted="deleteNoteHandler" > <template v-if="renderAvatarBadge" #avatar-badge> - <span class="badge badge-pill"> - {{ index + 1 }} - </span> + <design-note-pin + :label="index + 1" + class="user-avatar" + :is-resolved="discussion.resolved" + size="sm" + /> </template> </noteable-discussion> </ul> diff --git a/app/assets/javascripts/diffs/components/image_diff_overlay.vue b/app/assets/javascripts/diffs/components/image_diff_overlay.vue index eede8e52292..8871be1f9af 100644 --- a/app/assets/javascripts/diffs/components/image_diff_overlay.vue +++ b/app/assets/javascripts/diffs/components/image_diff_overlay.vue @@ -1,8 +1,8 @@ <script> -import { GlIcon } from '@gitlab/ui'; import { isArray } from 'lodash'; import { mapActions, mapGetters } from 'vuex'; import imageDiffMixin from 'ee_else_ce/diffs/mixins/image_diff'; +import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue'; function calcPercent(pos, renderedSize) { return (100 * pos) / renderedSize; @@ -11,7 +11,7 @@ function calcPercent(pos, renderedSize) { export default { name: 'ImageDiffOverlay', components: { - GlIcon, + DesignNotePin, }, mixins: [imageDiffMixin], props: { @@ -36,7 +36,7 @@ export default { badgeClass: { type: String, required: false, - default: 'badge badge-pill', + default: '', }, shouldToggleDiscussion: { type: Boolean, @@ -114,30 +114,28 @@ export default { > <span class="sr-only"> {{ __('Add image comment') }} </span> </button> - <button + + <design-note-pin v-for="(discussion, index) in allDiscussions" :key="discussion.id" - :style="getPosition(discussion)" - :class="[badgeClass, { 'is-draft': discussion.isDraft }]" - :disabled="!shouldToggleDiscussion" - class="js-image-badge" - type="button" + :label="showCommentIcon ? null : toggleText(discussion, index)" + :position="getPosition(discussion)" :aria-label="__('Show comments')" + class="js-image-badge" + :class="badgeClass" + :is-draft="discussion.isDraft" + :is-resolved="discussion.resolved" + is-on-image + :disabled="!shouldToggleDiscussion" @click="clickedToggle(discussion)" - > - <gl-icon v-if="showCommentIcon" name="image-comment-dark" :size="24" /> - <template v-else> - {{ toggleText(discussion, index) }} - </template> - </button> - <button + /> + + <design-note-pin v-if="canComment && currentCommentForm" - :style="{ left: `${currentCommentForm.xPercent}%`, top: `${currentCommentForm.yPercent}%` }" - :aria-label="__('Comment form position')" - class="btn-transparent comment-indicator position-absolute" - type="button" - > - <gl-icon name="image-comment-dark" :size="24" /> - </button> + :position="{ + left: `${currentCommentForm.xPercent}%`, + top: `${currentCommentForm.yPercent}%`, + }" + /> </div> </template> diff --git a/app/assets/javascripts/image_diff/helpers/badge_helper.js b/app/assets/javascripts/image_diff/helpers/badge_helper.js index 8ee72235a23..5ff00394e3b 100644 --- a/app/assets/javascripts/image_diff/helpers/badge_helper.js +++ b/app/assets/javascripts/image_diff/helpers/badge_helper.js @@ -14,7 +14,15 @@ export function createImageBadge(noteId, { x, y }, classNames = []) { } export function addImageBadge(containerEl, { coordinate, badgeText, noteId }) { - const buttonEl = createImageBadge(noteId, coordinate, ['badge', 'badge-pill']); + const buttonEl = createImageBadge(noteId, coordinate, [ + 'gl-display-flex', + 'gl-align-items-center', + 'gl-justify-content-center', + 'gl-font-sm', + 'design-note-pin', + 'on-image', + 'gl-absolute', + ]); buttonEl.textContent = badgeText; containerEl.appendChild(buttonEl); @@ -30,8 +38,8 @@ export function addImageCommentBadge(containerEl, { coordinate, noteId }) { export function addAvatarBadge(el, event) { const { noteId, badgeNumber } = event.detail; - // Add badge to new comment - const avatarBadgeEl = el.querySelector(`#${noteId} .badge`); + // Add design pin to new comment + const avatarBadgeEl = el.querySelector(`#${noteId} .design-note-pin`); avatarBadgeEl.textContent = badgeNumber; avatarBadgeEl.classList.remove('hidden'); } diff --git a/app/assets/javascripts/image_diff/helpers/dom_helper.js b/app/assets/javascripts/image_diff/helpers/dom_helper.js index a61e5f01f9b..3468a629f5a 100644 --- a/app/assets/javascripts/image_diff/helpers/dom_helper.js +++ b/app/assets/javascripts/image_diff/helpers/dom_helper.js @@ -10,12 +10,12 @@ export function setPositionDataAttribute(el, options) { } export function updateDiscussionAvatarBadgeNumber(discussionEl, newBadgeNumber) { - const avatarBadgeEl = discussionEl.querySelector('.image-diff-avatar-link .badge'); + const avatarBadgeEl = discussionEl.querySelector('.image-diff-avatar-link .design-note-pin'); avatarBadgeEl.textContent = newBadgeNumber; } export function updateDiscussionBadgeNumber(discussionEl, newBadgeNumber) { - const discussionBadgeEl = discussionEl.querySelector('.badge'); + const discussionBadgeEl = discussionEl.querySelector('.design-note-pin'); discussionBadgeEl.textContent = newBadgeNumber; } diff --git a/app/assets/javascripts/image_diff/image_diff.js b/app/assets/javascripts/image_diff/image_diff.js index a0dd8e6f894..e3ca4327efe 100644 --- a/app/assets/javascripts/image_diff/image_diff.js +++ b/app/assets/javascripts/image_diff/image_diff.js @@ -118,7 +118,7 @@ export default class ImageDiff { removeBadge(event) { const { badgeNumber } = event.detail; const indexToRemove = badgeNumber - 1; - const imageBadgeEls = this.imageFrameEl.querySelectorAll('.badge'); + const imageBadgeEls = this.imageFrameEl.querySelectorAll('.design-note-pin'); if (this.imageBadges.length !== badgeNumber) { // Cascade badges count numbers for (avatar badges + image badges) diff --git a/app/assets/javascripts/image_diff/replaced_image_diff.js b/app/assets/javascripts/image_diff/replaced_image_diff.js index a3d9b8a138a..8b84cc45c21 100644 --- a/app/assets/javascripts/image_diff/replaced_image_diff.js +++ b/app/assets/javascripts/image_diff/replaced_image_diff.js @@ -61,7 +61,7 @@ export default class ReplacedImageDiff extends ImageDiff { this.currentView = newView; // Clear existing badges on new view - const existingBadges = this.imageFrameEl.querySelectorAll('.badge'); + const existingBadges = this.imageFrameEl.querySelectorAll('.design-note-pin'); [...existingBadges].map((badge) => badge.remove()); // Remove existing references to old view image badges diff --git a/app/assets/javascripts/vue_shared/components/design_management/design_note_pin.vue b/app/assets/javascripts/vue_shared/components/design_management/design_note_pin.vue index cb038a8c4e1..c411496fad1 100644 --- a/app/assets/javascripts/vue_shared/components/design_management/design_note_pin.vue +++ b/app/assets/javascripts/vue_shared/components/design_management/design_note_pin.vue @@ -28,12 +28,37 @@ export default { required: false, default: false, }, + isOnImage: { + type: Boolean, + required: false, + default: false, + }, + isDraft: { + type: Boolean, + required: false, + default: false, + }, + size: { + type: String, + required: false, + default: 'md', + validator: (value) => ['sm', 'md'].includes(value), + }, + ariaLabel: { + type: String, + required: false, + default: null, + }, }, computed: { isNewNote() { return this.label === null; }, pinLabel() { + if (this.ariaLabel) { + return this.ariaLabel; + } + return this.isNewNote ? __('Comment form position') : sprintf(__("Comment '%{label}' position"), { label: this.label }); @@ -51,7 +76,10 @@ export default { 'js-image-badge design-note-pin': !isNewNote, resolved: isResolved, inactive: isInactive, + draft: isDraft, + 'on-image': isOnImage, 'gl-absolute': position, + small: size === 'sm', }" class="gl-display-flex gl-align-items-center gl-justify-content-center gl-font-sm" type="button" diff --git a/app/assets/stylesheets/components/design_management/design.scss b/app/assets/stylesheets/components/design_management/design.scss index 377d5130571..a9be1d89495 100644 --- a/app/assets/stylesheets/components/design_management/design.scss +++ b/app/assets/stylesheets/components/design_management/design.scss @@ -1,4 +1,5 @@ $design-pin-diameter: 28px; +$design-pin-diameter-sm: 24px; $t-gray-a-16-design-pin: rgba($black, 0.16); .layout-page.design-detail-layout { @@ -12,24 +13,6 @@ $t-gray-a-16-design-pin: rgba($black, 0.16); top: 35px; } - .design-note-pin { - display: flex; - height: $design-pin-diameter; - width: $design-pin-diameter; - box-sizing: content-box; - background-color: $purple-500; - color: $white; - font-weight: $gl-font-weight-bold; - border-radius: 50%; - z-index: 1; - padding: 0; - border: 0; - - &.resolved { - background-color: $gray-500; - } - } - .comment-indicator { border-radius: 50%; } @@ -40,35 +23,6 @@ $t-gray-a-16-design-pin: rgba($black, 0.16); cursor: grabbing; } } - - /** - * Design pin that overlays the design - */ - .frame .design-note-pin { - box-shadow: 0 2px 4px $t-gray-a-08, 0 0 1px $t-gray-a-24; - border: $white 2px solid; - will-change: transform, box-shadow, opacity; - // NOTE: verbose transition property required for Safari - transition: transform $general-hover-transition-duration linear, box-shadow $general-hover-transition-duration linear, opacity $general-hover-transition-duration linear; - transform-origin: 0 0; - transform: translate(-50%, -50%); - - &:hover { - transform: scale(1.2) translate(-50%, -50%); - } - - &:active { - box-shadow: 0 0 4px $t-gray-a-16-design-pin, 0 4px 12px $t-gray-a-16-design-pin; - } - - &.inactive { - @include gl-opacity-5; - - &:hover { - @include gl-opacity-10; - } - } - } } .design-scaler-wrapper { @@ -177,3 +131,63 @@ $t-gray-a-16-design-pin: rgba($black, 0.16); .design-card-header { background: transparent; } + +.design-note-pin { + display: flex; + height: $design-pin-diameter; + width: $design-pin-diameter; + box-sizing: content-box; + background-color: $purple-500; + color: $white; + font-weight: $gl-font-weight-bold; + border-radius: 50%; + z-index: 1; + padding: 0; + border: 0; + + &.draft { + background-color: $orange-500; + } + + &.resolved { + background-color: $gray-500; + } + + &.on-image { + box-shadow: 0 2px 4px $t-gray-a-08, 0 0 1px $t-gray-a-24; + border: $white 2px solid; + will-change: transform, box-shadow, opacity; + // NOTE: verbose transition property required for Safari + transition: transform $general-hover-transition-duration linear, box-shadow $general-hover-transition-duration linear, opacity $general-hover-transition-duration linear; + transform-origin: 0 0; + transform: translate(-50%, -50%); + + &:hover { + transform: scale(1.2) translate(-50%, -50%); + } + + &:active { + box-shadow: 0 0 4px $t-gray-a-16-design-pin, 0 4px 12px $t-gray-a-16-design-pin; + } + + &.inactive { + @include gl-opacity-5; + + &:hover { + @include gl-opacity-10; + } + } + } + + &.small { + position: absolute; + border: 1px solid $white; + height: $design-pin-diameter-sm; + width: $design-pin-diameter-sm; + } + + &.user-avatar { + top: 25px; + right: 8px; + } +} diff --git a/app/assets/stylesheets/framework/diffs.scss b/app/assets/stylesheets/framework/diffs.scss index 0e849f8d635..1f4221e5778 100644 --- a/app/assets/stylesheets/framework/diffs.scss +++ b/app/assets/stylesheets/framework/diffs.scss @@ -1072,24 +1072,6 @@ table.code { } } -.frame .badge.badge-pill, -.image-diff-avatar-link .badge.badge-pill, -.user-avatar-link .badge.badge-pill, -.notes > .badge.badge-pill { - position: absolute; - background-color: $blue-400; - color: $white; - border: $white 1px solid; - min-height: $gl-padding; - padding: 5px 8px; - border-radius: 12px; - - &:focus { - outline: none; - } -} - -.frame .badge.badge-pill, .frame .image-comment-badge, .frame .comment-indicator { // Center align badges on the frame @@ -1121,11 +1103,6 @@ table.code { } } -.notes > .badge.badge-pill { - display: none; - left: -13px; -} - .discussion-notes { min-height: 35px; @@ -1134,18 +1111,22 @@ table.code { min-height: 25px; } + .diff-notes-expand { + display: none; + } + &.collapsed { background-color: $white; + .diff-notes-expand { + display: initial; + } + .diff-notes-collapse, .note, .discussion-reply-holder { display: none; } - - .notes > .badge.badge-pill { - display: block; - } } } diff --git a/app/events/members/members_added_event.rb b/app/events/members/members_added_event.rb new file mode 100644 index 00000000000..c174ffebab6 --- /dev/null +++ b/app/events/members/members_added_event.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Members + class MembersAddedEvent < ::Gitlab::EventStore::Event + def schema + { + 'type' => 'object', + 'required' => %w[source_id source_type], + 'properties' => { + 'source_id' => { 'type' => 'integer' }, + 'source_type' => { 'type' => 'string' } + } + } + end + end +end diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index 16332b654d4..6a1adc478a5 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -125,6 +125,12 @@ module Types field :archived, GraphQL::Types::Boolean, null: true, method: :archived?, description: 'Whether the current project is archived.' + field :language, GraphQL::Types::String, + description: 'Blob language.', + method: :blob_language, + null: true, + calls_gitaly: true + def raw_text_blob object.data unless object.binary? end diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb index efd29b5601b..47b72df32a2 100644 --- a/app/presenters/blob_presenter.rb +++ b/app/presenters/blob_presenter.rb @@ -32,7 +32,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated end def blob_language - @_blob_language ||= Gitlab::Diff::CustomDiff.transformed_blob_language(blob) || language + @_blob_language ||= Gitlab::Diff::CustomDiff.transformed_blob_language(blob) || gitattr_language || detect_language end def raw_plain_data @@ -166,9 +166,15 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated @all_lines ||= blob.data.lines end - def language + def gitattr_language blob.language_from_gitattributes end + + def detect_language + return if blob.binary? + + Rouge::Lexer.guess(filename: blob.path, source: blob_data(nil)) { |lex| lex.min_by(&:tag) }.tag + end end BlobPresenter.prepend_mod_with('BlobPresenter') diff --git a/app/presenters/snippet_blob_presenter.rb b/app/presenters/snippet_blob_presenter.rb index 026d442291c..51ce6ccea58 100644 --- a/app/presenters/snippet_blob_presenter.rb +++ b/app/presenters/snippet_blob_presenter.rb @@ -33,7 +33,7 @@ class SnippetBlobPresenter < BlobPresenter blob.container end - def language + def gitattr_language nil end diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb index acd00d0d1ec..dc29bb2c6da 100644 --- a/app/services/members/create_service.rb +++ b/app/services/members/create_service.rb @@ -24,6 +24,9 @@ module Members add_members enqueue_onboarding_progress_action + + publish_event! + result rescue BlankInvitesError, TooManyInvitesError, MembershipLockedError => e error(e.message) @@ -144,6 +147,15 @@ module Members def formatted_errors errors.to_sentence end + + def publish_event! + Gitlab::EventStore.publish( + Members::MembersAddedEvent.new(data: { + source_id: source.id, + source_type: source.class.name + }) + ) + end end end diff --git a/app/views/discussions/_notes.html.haml b/app/views/discussions/_notes.html.haml index beac4946fd7..a35ba12dd52 100644 --- a/app/views/discussions/_notes.html.haml +++ b/app/views/discussions/_notes.html.haml @@ -9,9 +9,9 @@ -# to the first note position when we click on a badge diff discussion %ul.notes{ id: "discussion_#{discussion.id}", data: { discussion_id: discussion.id, position: discussion.notes[0].position.to_json } } - if discussion.try(:on_image?) && show_toggle - %button.gl-button.diff-notes-collapse.js-diff-notes-toggle{ type: 'button' } + %button.comment-indicator.gl-display-flex.gl-align-items-center.gl-justify-content-center.gl-font-sm.diff-notes-collapse.js-diff-notes-toggle{ type: 'button' } = sprite_icon('collapse', css_class: 'collapse-icon') - %button.gl-button.btn-transparent.badge.badge-pill.js-diff-notes-toggle{ type: 'button' } + %button.gl-align-items-center.gl-justify-content-center.gl-font-sm.small.gl-translate-x-n50.design-note-pin.js-diff-notes-toggle.diff-notes-expand{ type: 'button' } = badge_counter = render partial: "shared/notes/note", collection: discussion.notes, as: :note, locals: { badge_counter: badge_counter, show_image_comment_badge: show_image_comment_badge } diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index 0b5da84e4e3..ac238878392 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -64,43 +64,13 @@ - experiment(:new_project_sast_enabled, user: current_user) do |e| - e.try(:candidate) do - .form-group - .form-check.gl-mb-3 - = check_box_tag 'project[initialize_with_sast]', '1', true, class: 'form-check-input', data: { qa_selector: 'initialize_with_sast_checkbox', track_experiment: e.name, track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' } - = label_tag 'project[initialize_with_sast]', class: 'form-check-label' do - = s_('ProjectsNew|Enable Static Application Security Testing (SAST)') - .form-text.text-muted - = s_('ProjectsNew|Analyze your source code for known security vulnerabilities.') - = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed', track_experiment: e.name } + = render 'new_project_initialize_with_sast', experiment_name: e.name, track_label: track_label, checked: true, with_free_badge: false - e.try(:unchecked_candidate) do - .form-group - .form-check.gl-mb-3 - = check_box_tag 'project[initialize_with_sast]', '1', false, class: 'form-check-input', data: { qa_selector: 'initialize_with_sast_checkbox', track_experiment: e.name, track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' } - = label_tag 'project[initialize_with_sast]', class: 'form-check-label' do - = s_('ProjectsNew|Enable Static Application Security Testing (SAST)') - .form-text.text-muted - = s_('ProjectsNew|Analyze your source code for known security vulnerabilities.') - = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed', track_experiment: e.name } + = render 'new_project_initialize_with_sast', experiment_name: e.name, track_label: track_label, checked: false, with_free_badge: false - e.try(:free_indicator) do - .form-group - .form-check.gl-mb-3 - = check_box_tag 'project[initialize_with_sast]', '1', true, class: 'form-check-input', data: { qa_selector: 'initialize_with_sast_checkbox', track_experiment: e.name, track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' } - = label_tag 'project[initialize_with_sast]', class: 'form-check-label' do - = s_('ProjectsNew|Enable Static Application Security Testing (SAST)') - = gl_badge_tag _('Free'), variant: :info, size: :sm - .form-text.text-muted - = s_('ProjectsNew|Analyze your source code for known security vulnerabilities.') - = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed', track_experiment: e.name } + = render 'new_project_initialize_with_sast', experiment_name: e.name, track_label: track_label, checked: true, with_free_badge: true - e.try(:unchecked_free_indicator) do - .form-group - .form-check.gl-mb-3 - = check_box_tag 'project[initialize_with_sast]', '1', false, class: 'form-check-input', data: { qa_selector: 'initialize_with_sast_checkbox', track_experiment: e.name, track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' } - = label_tag 'project[initialize_with_sast]', class: 'form-check-label' do - = s_('ProjectsNew|Enable Static Application Security Testing (SAST)') - = gl_badge_tag _('Free'), variant: :info, size: :sm - .form-text.text-muted - = s_('ProjectsNew|Analyze your source code for known security vulnerabilities.') - = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed', track_experiment: e.name } + = render 'new_project_initialize_with_sast', experiment_name: e.name, track_label: track_label, checked: false, with_free_badge: true = f.submit _('Create project'), class: "btn gl-button btn-confirm", data: { track_label: "#{track_label}", track_action: "click_button", track_property: "create_project", track_value: "" } = link_to _('Cancel'), dashboard_projects_path, class: 'btn gl-button btn-default btn-cancel', data: { track_label: "#{track_label}", track_action: "click_button", track_property: "cancel", track_value: "" } diff --git a/app/views/projects/_new_project_initialize_with_sast.html.haml b/app/views/projects/_new_project_initialize_with_sast.html.haml new file mode 100644 index 00000000000..ec12abbf789 --- /dev/null +++ b/app/views/projects/_new_project_initialize_with_sast.html.haml @@ -0,0 +1,16 @@ +- experiment_name = local_assigns.fetch(:experiment_name) +- track_label = local_assigns.fetch(:track_label) + +- with_free_badge = local_assigns.fetch(:with_free_badge, false) +- checked = local_assigns.fetch(:checked, false) + +.form-group + .form-check.gl-mb-3 + = check_box_tag 'project[initialize_with_sast]', '1', checked, class: 'form-check-input', data: { qa_selector: 'initialize_with_sast_checkbox', track_experiment: experiment_name, track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' } + = label_tag 'project[initialize_with_sast]', class: 'form-check-label' do + = s_('ProjectsNew|Enable Static Application Security Testing (SAST)') + - if with_free_badge + = gl_badge_tag _('Free'), variant: :info, size: :sm + .form-text.text-muted + = s_('ProjectsNew|Analyze your source code for known security vulnerabilities.') + = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed', track_experiment: experiment_name } diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index 6549c86ab29..3ab8514aebf 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -25,7 +25,7 @@ - elsif note_counter == 0 - counter = badge_counter if local_assigns[:badge_counter] - badge_class = "hidden" if @fresh_discussion || counter.nil? - %span.badge.badge-pill{ class: badge_class } + %span.gl-display-flex.gl-align-items-center.gl-justify-content-center.gl-font-sm.design-note-pin.small.user-avatar{ class: badge_class } = counter .timeline-content .note-header |