summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/batch_comments/components/diff_file_drafts.vue11
-rw-r--r--app/assets/javascripts/deprecated_notes.js2
-rw-r--r--app/assets/javascripts/design_management/components/design_overlay.vue1
-rw-r--r--app/assets/javascripts/diffs/components/diff_discussions.vue31
-rw-r--r--app/assets/javascripts/diffs/components/image_diff_overlay.vue44
-rw-r--r--app/assets/javascripts/image_diff/helpers/badge_helper.js14
-rw-r--r--app/assets/javascripts/image_diff/helpers/dom_helper.js4
-rw-r--r--app/assets/javascripts/image_diff/image_diff.js2
-rw-r--r--app/assets/javascripts/image_diff/replaced_image_diff.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/design_management/design_note_pin.vue28
-rw-r--r--app/assets/stylesheets/components/design_management/design.scss108
-rw-r--r--app/assets/stylesheets/framework/diffs.scss35
-rw-r--r--app/events/members/members_added_event.rb16
-rw-r--r--app/graphql/types/repository/blob_type.rb6
-rw-r--r--app/presenters/blob_presenter.rb10
-rw-r--r--app/presenters/snippet_blob_presenter.rb2
-rw-r--r--app/services/members/create_service.rb12
-rw-r--r--app/views/discussions/_notes.html.haml4
-rw-r--r--app/views/projects/_new_project_fields.html.haml38
-rw-r--r--app/views/projects/_new_project_initialize_with_sast.html.haml16
-rw-r--r--app/views/shared/notes/_note.html.haml2
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