Skip to content
Snippets Groups Projects
Verified Commit d2fd6bd5 authored by Phil Hughes's avatar Phil Hughes
Browse files

Added diff suggestion popover

parent 57783259
No related branches found
No related tags found
No related merge requests found
Showing
with 207 additions and 75 deletions
Loading
Loading
@@ -69,6 +69,16 @@ export default {
required: false,
default: false,
},
dismissEndpoint: {
type: String,
required: false,
default: '',
},
showSuggestPopover: {
type: Boolean,
required: false,
default: false,
},
},
data() {
const treeWidth =
Loading
Loading
@@ -141,7 +151,12 @@ export default {
showTreeList: 'adjustView',
},
mounted() {
this.setBaseConfig({ endpoint: this.endpoint, projectPath: this.projectPath });
this.setBaseConfig({
endpoint: this.endpoint,
projectPath: this.projectPath,
dismissEndpoint: this.dismissEndpoint,
showSuggestPopover: this.showSuggestPopover,
});
 
if (this.shouldShow) {
this.fetchData();
Loading
Loading
Loading
Loading
@@ -42,6 +42,7 @@ export default {
noteableData: state => state.notes.noteableData,
diffViewType: state => state.diffs.diffViewType,
}),
...mapState('diffs', ['showSuggestPopover']),
...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters([
'isLoggedIn',
Loading
Loading
@@ -80,7 +81,12 @@ export default {
}
},
methods: {
...mapActions('diffs', ['cancelCommentForm', 'assignDiscussionsToDiff', 'saveDiffDiscussion']),
...mapActions('diffs', [
'cancelCommentForm',
'assignDiscussionsToDiff',
'saveDiffDiscussion',
'setSuggestPopoverDismissed',
]),
handleCancelCommentForm(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
const msg = s__('Notes|Are you sure you want to cancel creating this comment?');
Loading
Loading
@@ -125,11 +131,13 @@ export default {
:line="line"
:help-page-path="helpPagePath"
:diff-file="diffFile"
:show-suggest-popover="showSuggestPopover"
save-button-title="Comment"
class="diff-comment-form"
@handleFormUpdateAddToReview="addToReview"
@cancelForm="handleCancelCommentForm"
@handleFormUpdate="handleSaveNote"
@handleSuggestDismissed="setSuggestPopoverDismissed"
/>
</div>
</template>
Loading
Loading
@@ -72,6 +72,8 @@ export default function initDiffsApp(store) {
currentUser: JSON.parse(dataset.currentUserData) || {},
changesEmptyStateIllustration: dataset.changesEmptyStateIllustration,
isFluidLayout: parseBoolean(dataset.isFluidLayout),
dismissEndpoint: dataset.dismissEndpoint,
showSuggestPopover: parseBoolean(dataset.showSuggestPopover),
};
},
computed: {
Loading
Loading
@@ -99,6 +101,8 @@ export default function initDiffsApp(store) {
shouldShow: this.activeTab === 'diffs',
changesEmptyStateIllustration: this.changesEmptyStateIllustration,
isFluidLayout: this.isFluidLayout,
dismissEndpoint: this.dismissEndpoint,
showSuggestPopover: this.showSuggestPopover,
},
});
},
Loading
Loading
Loading
Loading
@@ -36,8 +36,8 @@ import {
import { diffViewerModes } from '~/ide/constants';
 
export const setBaseConfig = ({ commit }, options) => {
const { endpoint, projectPath } = options;
commit(types.SET_BASE_CONFIG, { endpoint, projectPath });
const { endpoint, projectPath, dismissEndpoint, showSuggestPopover } = options;
commit(types.SET_BASE_CONFIG, { endpoint, projectPath, dismissEndpoint, showSuggestPopover });
};
 
export const fetchDiffFiles = ({ state, commit }) => {
Loading
Loading
@@ -455,5 +455,17 @@ export const toggleFullDiff = ({ dispatch, getters, state }, filePath) => {
export const setFileCollapsed = ({ commit }, { filePath, collapsed }) =>
commit(types.SET_FILE_COLLAPSED, { filePath, collapsed });
 
export const setSuggestPopoverDismissed = ({ commit, state }) =>
axios
.post(state.dismissEndpoint, {
feature_name: 'suggest_popover_dismissed',
})
.then(() => {
commit(types.SET_SHOW_SUGGEST_POPOVER);
})
.catch(() => {
createFlash(s__('MergeRequest|Error dismissing suggestion popover. Please try again.'));
});
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
Loading
Loading
@@ -28,4 +28,6 @@ export default () => ({
renderTreeList: true,
showWhitespace: true,
fileFinderVisible: false,
dismissEndpoint: '',
showSuggestPopover: true,
});
Loading
Loading
@@ -33,3 +33,5 @@ export const SET_HIDDEN_VIEW_DIFF_FILE_LINES = 'SET_HIDDEN_VIEW_DIFF_FILE_LINES'
export const SET_CURRENT_VIEW_DIFF_FILE_LINES = 'SET_CURRENT_VIEW_DIFF_FILE_LINES';
export const ADD_CURRENT_VIEW_DIFF_FILE_LINES = 'ADD_CURRENT_VIEW_DIFF_FILE_LINES';
export const TOGGLE_DIFF_FILE_RENDERING_MORE = 'TOGGLE_DIFF_FILE_RENDERING_MORE';
export const SET_SHOW_SUGGEST_POPOVER = 'SET_SHOW_SUGGEST_POPOVER';
Loading
Loading
@@ -11,8 +11,8 @@ import * as types from './mutation_types';
 
export default {
[types.SET_BASE_CONFIG](state, options) {
const { endpoint, projectPath } = options;
Object.assign(state, { endpoint, projectPath });
const { endpoint, projectPath, dismissEndpoint, showSuggestPopover } = options;
Object.assign(state, { endpoint, projectPath, dismissEndpoint, showSuggestPopover });
},
 
[types.SET_LOADING](state, isLoading) {
Loading
Loading
@@ -302,4 +302,7 @@ export default {
 
file.renderingLines = !file.renderingLines;
},
[types.SET_SHOW_SUGGEST_POPOVER](state) {
state.showSuggestPopover = false;
},
};
Loading
Loading
@@ -77,6 +77,11 @@ export default {
required: false,
default: '',
},
showSuggestPopover: {
type: Boolean,
required: false,
default: false,
},
},
data() {
let updatedNoteBody = this.noteBody;
Loading
Loading
@@ -247,6 +252,8 @@ export default {
:can-suggest="canSuggest"
:add-spacing-classes="false"
:help-page-path="helpPagePath"
:show-suggest-popover="showSuggestPopover"
@handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
>
<textarea
id="note_note"
Loading
Loading
@@ -303,7 +310,7 @@ export default {
{{ __('Add comment now') }}
</button>
<button
class="btn btn-cancel note-edit-cancel js-close-discussion-note-form"
class="btn note-edit-cancel js-close-discussion-note-form"
type="button"
@click="cancelHandler()"
>
Loading
Loading
Loading
Loading
@@ -67,6 +67,11 @@ export default {
required: false,
default: '',
},
showSuggestPopover: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
Loading
Loading
@@ -194,8 +199,10 @@ export default {
:preview-markdown="previewMarkdown"
:line-content="lineContent"
:can-suggest="canSuggest"
:show-suggest-popover="showSuggestPopover"
@preview-markdown="showPreviewTab"
@write-markdown="showWriteTab"
@handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
/>
<div v-show="!previewMarkdown" class="md-write-holder">
<div class="zen-backdrop">
Loading
Loading
<script>
import $ from 'jquery';
import { GlTooltipDirective } from '@gitlab/ui';
import { GlPopover, GlButton, GlTooltipDirective } from '@gitlab/ui';
import ToolbarButton from './toolbar_button.vue';
import Icon from '../icon.vue';
 
Loading
Loading
@@ -8,6 +8,8 @@ export default {
components: {
ToolbarButton,
Icon,
GlPopover,
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
Loading
Loading
@@ -27,6 +29,11 @@ export default {
required: false,
default: true,
},
showSuggestPopover: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
mdTable() {
Loading
Loading
@@ -70,6 +77,9 @@ export default {
 
this.$emit('write-markdown');
},
handleSuggestDismissed() {
this.$emit('handleSuggestDismissed');
},
},
};
</script>
Loading
Loading
@@ -93,66 +103,92 @@ export default {
</button>
</li>
<li :class="{ active: !previewMarkdown }" class="md-header-toolbar">
<toolbar-button tag="**" :button-title="__('Add bold text')" icon="bold" />
<toolbar-button tag="*" :button-title="__('Add italic text')" icon="italic" />
<toolbar-button
:prepend="true"
tag="> "
:button-title="__('Insert a quote')"
icon="quote"
/>
<toolbar-button tag="`" tag-block="```" :button-title="__('Insert code')" icon="code" />
<toolbar-button
tag="[{text}](url)"
tag-select="url"
:button-title="__('Add a link')"
icon="link"
/>
<toolbar-button
:prepend="true"
tag="* "
:button-title="__('Add a bullet list')"
icon="list-bulleted"
/>
<toolbar-button
:prepend="true"
tag="1. "
:button-title="__('Add a numbered list')"
icon="list-numbered"
/>
<toolbar-button
:prepend="true"
tag="* [ ] "
:button-title="__('Add a task list')"
icon="task-done"
/>
<toolbar-button
:tag="mdTable"
:prepend="true"
:button-title="__('Add a table')"
icon="table"
/>
<toolbar-button
v-if="canSuggest"
:tag="mdSuggestion"
:prepend="true"
:button-title="__('Insert suggestion')"
:cursor-offset="4"
:tag-content="lineContent"
icon="doc-code"
class="qa-suggestion-btn"
/>
<button
v-gl-tooltip
:aria-label="__('Go full screen')"
class="toolbar-btn toolbar-fullscreen-btn js-zen-enter"
data-container="body"
tabindex="-1"
:title="__('Go full screen')"
type="button"
>
<icon name="screen-full" />
</button>
<div class="d-inline-block">
<toolbar-button tag="**" :button-title="__('Add bold text')" icon="bold" />
<toolbar-button tag="*" :button-title="__('Add italic text')" icon="italic" />
<toolbar-button
:prepend="true"
tag="> "
:button-title="__('Insert a quote')"
icon="quote"
/>
</div>
<div class="d-inline-block ml-md-2 ml-0">
<template v-if="canSuggest">
<toolbar-button
ref="suggestButton"
:tag="mdSuggestion"
:prepend="true"
:button-title="__('Insert suggestion')"
:cursor-offset="4"
:tag-content="lineContent"
icon="doc-code"
class="qa-suggestion-btn"
@click="handleSuggestDismissed"
/>
<gl-popover
v-if="showSuggestPopover"
:target="() => $refs.suggestButton"
:css-classes="['diff-suggest-popover']"
placement="bottom"
:show="showSuggestPopover"
>
<strong>{{ __('New! Suggest changes directly') }}</strong>
<p class="mb-2">
{{ __('Suggest code changes which are immediately applied. Try it out!') }}
</p>
<gl-button variant="primary" size="sm" @click="handleSuggestDismissed">
{{ __('Got it') }}
</gl-button>
</gl-popover>
</template>
<toolbar-button tag="`" tag-block="```" :button-title="__('Insert code')" icon="code" />
<toolbar-button
tag="[{text}](url)"
tag-select="url"
:button-title="__('Add a link')"
icon="link"
/>
</div>
<div class="d-inline-block ml-md-2 ml-0">
<toolbar-button
:prepend="true"
tag="* "
:button-title="__('Add a bullet list')"
icon="list-bulleted"
/>
<toolbar-button
:prepend="true"
tag="1. "
:button-title="__('Add a numbered list')"
icon="list-numbered"
/>
<toolbar-button
:prepend="true"
tag="* [ ] "
:button-title="__('Add a task list')"
icon="task-done"
/>
<toolbar-button
:tag="mdTable"
:prepend="true"
:button-title="__('Add a table')"
icon="table"
/>
</div>
<div class="d-inline-block ml-md-2 ml-0">
<button
v-gl-tooltip
:aria-label="__('Go full screen')"
class="toolbar-btn toolbar-fullscreen-btn js-zen-enter"
data-container="body"
tabindex="-1"
:title="__('Go full screen')"
type="button"
>
<icon name="screen-full" />
</button>
</div>
</li>
</ul>
</div>
Loading
Loading
Loading
Loading
@@ -66,6 +66,7 @@ export default {
class="toolbar-btn js-md"
tabindex="-1"
data-container="body"
@click="() => $emit('click')"
>
<icon :name="icon" />
</button>
Loading
Loading
Loading
Loading
@@ -154,11 +154,9 @@
}
 
.toolbar-fullscreen-btn {
margin-left: $gl-padding;
margin-right: -5px;
 
@include media-breakpoint-down(xs) {
margin-left: 0;
margin-right: 0;
}
}
Loading
Loading
Loading
Loading
@@ -14,7 +14,7 @@
position: -webkit-sticky;
position: sticky;
top: $mr-file-header-top;
z-index: 102;
z-index: 220;
 
&::before {
content: '';
Loading
Loading
@@ -1122,3 +1122,15 @@ table.code {
outline: 0;
}
}
.diff-suggest-popover {
&.popover {
width: 250px;
min-width: 250px;
z-index: 210;
}
.popover-header {
display: none;
}
}
Loading
Loading
@@ -3,6 +3,7 @@
module UserCalloutsHelper
GKE_CLUSTER_INTEGRATION = 'gke_cluster_integration'.freeze
GCP_SIGNUP_OFFER = 'gcp_signup_offer'.freeze
SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed'.freeze
 
def show_gke_cluster_integration_callout?(project)
can?(current_user, :create_cluster, project) &&
Loading
Loading
@@ -20,6 +21,10 @@ module UserCalloutsHelper
def render_dashboard_gold_trial(user)
end
 
def show_suggest_popover?
!user_dismissed?(SUGGEST_POPOVER_DISMISSED)
end
private
 
def user_dismissed?(feature_name)
Loading
Loading
Loading
Loading
@@ -10,7 +10,8 @@ module UserCalloutEnums
{
gke_cluster_integration: 1,
gcp_signup_offer: 2,
cluster_security_warning: 3
cluster_security_warning: 3,
suggest_popover_dismissed: 4
}
end
end
Loading
Loading
@@ -80,7 +80,9 @@
current_user_data: UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestUserEntity).to_json,
project_path: project_path(@merge_request.project),
changes_empty_state_illustration: image_path('illustrations/merge_request_changes_empty.svg'),
is_fluid_layout: fluid_layout.to_s } }
is_fluid_layout: fluid_layout.to_s,
dismiss_endpoint: user_callouts_path,
show_suggest_popover: show_suggest_popover?.to_s } }
 
.mr-loading-status
= spinner
Loading
Loading
---
title: Added diff suggestion feature discovery popover
merge_request:
author:
type: added
Loading
Loading
@@ -4761,6 +4761,9 @@ msgstr ""
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
 
msgid "Got it"
msgstr ""
msgid "Got it!"
msgstr ""
 
Loading
Loading
@@ -6153,6 +6156,9 @@ msgstr ""
msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
msgstr ""
 
msgid "MergeRequest|Error dismissing suggestion popover. Please try again."
msgstr ""
msgid "MergeRequest|Error loading full diff. Please try again."
msgstr ""
 
Loading
Loading
@@ -6509,6 +6515,9 @@ msgstr ""
msgid "New users set to external"
msgstr ""
 
msgid "New! Suggest changes directly"
msgstr ""
msgid "New..."
msgstr ""
 
Loading
Loading
@@ -9643,6 +9652,9 @@ msgstr ""
msgid "Successfully unlocked"
msgstr ""
 
msgid "Suggest code changes which are immediately applied. Try it out!"
msgstr ""
msgid "Suggested change"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -32,7 +32,7 @@ describe 'Issue markdown toolbar', :js do
find('.js-main-target-form #note-body')
page.evaluate_script('document.querySelectorAll(".js-main-target-form #note-body")[0].setSelectionRange(4, 50)')
 
find('.toolbar-btn:nth-child(2)').click
all('.toolbar-btn')[1].click
 
expect(find('#note-body')[:value]).to eq("test\n*underline*\n")
end
Loading
Loading
Loading
Loading
@@ -141,7 +141,7 @@ describe 'Merge request > User posts notes', :js do
 
page.within('.current-note-edit-form') do
expect(find('#note_note').value).to eq('This is the new content')
find('.js-md:first-child').click
first('.js-md').click
expect(find('#note_note').value).to eq('This is the new content****')
end
end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment