diff --git a/app/assets/javascripts/behaviors/markdown/highlight_current_user.js b/app/assets/javascripts/behaviors/markdown/highlight_current_user.js new file mode 100644 index 0000000000000000000000000000000000000000..6208b3f0032a1be6619f3936fae7266d285c20db --- /dev/null +++ b/app/assets/javascripts/behaviors/markdown/highlight_current_user.js @@ -0,0 +1,17 @@ +/** + * Highlights the current user in existing elements with a user ID data attribute. + * + * @param elements DOM elements that represent user mentions + */ +export default function highlightCurrentUser(elements) { + const currentUserId = gon && gon.current_user_id; + if (!currentUserId) { + return; + } + + elements.forEach(element => { + if (parseInt(element.dataset.user, 10) === currentUserId) { + element.classList.add('current-user'); + } + }); +} diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js index 429455f97ece77c447ca9001a9fe8a154e5b4a10..a2d4331b6d1b4ce03e3094a702387f016e4e74c7 100644 --- a/app/assets/javascripts/behaviors/markdown/render_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import syntaxHighlight from '~/syntax_highlight'; import renderMath from './render_math'; import renderMermaid from './render_mermaid'; +import highlightCurrentUser from './highlight_current_user'; // Render GitLab flavoured Markdown // @@ -11,6 +12,7 @@ $.fn.renderGFM = function renderGFM() { syntaxHighlight(this.find('.js-syntax-highlight')); renderMath(this.find('.js-render-math')); renderMermaid(this.find('.js-render-mermaid')); + highlightCurrentUser(this.find('.gfm-project_member').get()); return this; }; diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index d8e8efb982a43821e4f9bbfb20a02172e2e05307..618a1581d8f6daac56b7f8d9aaf55e08adfced3b 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -11,6 +11,7 @@ import commentForm from './comment_form.vue'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; import skeletonLoadingContainer from '../../vue_shared/components/notes/skeleton_note.vue'; +import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user'; export default { name: 'NotesApp', @@ -96,6 +97,9 @@ export default { }); } }, + updated() { + this.$nextTick(() => highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member'))); + }, methods: { ...mapActions({ fetchDiscussions: 'fetchDiscussions', diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss index d2ba76f516016b447e9c1b070c768330ff3e5bf2..50d4298d418f1cdf4bf3f37b73a31f46a7374ae7 100644 --- a/app/assets/stylesheets/framework/gfm.scss +++ b/app/assets/stylesheets/framework/gfm.scss @@ -11,6 +11,10 @@ padding: 0 2px; background-color: $blue-100; border-radius: $border-radius-default; + + &.current-user { + background-color: $orange-100; + } } .gfm-color_chip { diff --git a/changelogs/unreleased/winh-highlight-current-user.yml b/changelogs/unreleased/winh-highlight-current-user.yml new file mode 100644 index 0000000000000000000000000000000000000000..125a5c08c4e1b59b8126904eb394ccb6e9f78d90 --- /dev/null +++ b/changelogs/unreleased/winh-highlight-current-user.yml @@ -0,0 +1,5 @@ +--- +title: Highlight current user in comments +merge_request: 21406 +author: +type: changed diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb index f08c73f947cb0f9814f3ed556f802e92204f649c..fed77453cbb7d68d926450da6b1eb49502d3f9e8 100644 --- a/spec/features/issues/notes_on_issues_spec.rb +++ b/spec/features/issues/notes_on_issues_spec.rb @@ -3,6 +3,12 @@ require 'spec_helper' describe 'Create notes on issues', :js do let(:user) { create(:user) } + def submit_comment(text) + fill_in 'note[note]', with: text + click_button 'Comment' + wait_for_requests + end + shared_examples 'notes with reference' do let(:issue) { create(:issue, project: project) } let(:note_text) { "Check #{mention.to_reference}" } @@ -12,10 +18,7 @@ describe 'Create notes on issues', :js do sign_in(user) visit project_issue_path(project, issue) - fill_in 'note[note]', with: note_text - click_button 'Comment' - - wait_for_requests + submit_comment(note_text) end it 'creates a note with reference and cross references the issue' do @@ -74,4 +77,16 @@ describe 'Create notes on issues', :js do let(:mention) { create(:merge_request, source_project: project) } end end + + it 'highlights the current user in a comment' do + project = create(:project) + issue = create(:issue, project: project) + project.add_developer(user) + sign_in(user) + + visit project_issue_path(project, issue) + submit_comment("@#{user.username} note to self") + + expect(page).to have_selector '.gfm-project_member.current-user', text: user.username + end end diff --git a/spec/javascripts/behaviors/markdown/highlight_current_user_spec.js b/spec/javascripts/behaviors/markdown/highlight_current_user_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..3305ddc412d885274ddfc463e697557b674f23e7 --- /dev/null +++ b/spec/javascripts/behaviors/markdown/highlight_current_user_spec.js @@ -0,0 +1,55 @@ +import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user'; + +describe('highlightCurrentUser', () => { + let rootElement; + let elements; + + beforeEach(() => { + setFixtures(` +
+
@first
+
@second
+
+ `); + rootElement = document.getElementById('dummy-root-element'); + elements = rootElement.querySelectorAll('[data-user]'); + }); + + describe('without current user', () => { + beforeEach(() => { + window.gon = window.gon || {}; + window.gon.current_user_id = null; + }); + + afterEach(() => { + delete window.gon.current_user_id; + }); + + it('does not highlight the user', () => { + const initialHtml = rootElement.outerHTML; + + highlightCurrentUser(elements); + + expect(rootElement.outerHTML).toBe(initialHtml); + }); + }); + + describe('with current user', () => { + beforeEach(() => { + window.gon = window.gon || {}; + window.gon.current_user_id = 2; + }); + + afterEach(() => { + delete window.gon.current_user_id; + }); + + it('highlights current user', () => { + highlightCurrentUser(elements); + + expect(elements.length).toBe(2); + expect(elements[0]).not.toHaveClass('current-user'); + expect(elements[1]).toHaveClass('current-user'); + }); + }); +});