Read about our upcoming Code of Conduct on this issue

Commit 9ffe2bf1 authored by Fabian Schneider's avatar Fabian Schneider Committed by Fatih Acet
Browse files

Resolve "Drop down filter for project snippets"

parent 4f765d57eea8
......@@ -15,6 +15,7 @@ export const defaultAutocompleteConfig = {
epics: true,
milestones: true,
labels: true,
snippets: true,
};
class GfmAutoComplete {
......@@ -50,6 +51,7 @@ class GfmAutoComplete {
if (this.enableMap.milestones) this.setupMilestones($input);
if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
if (this.enableMap.labels) this.setupLabels($input);
if (this.enableMap.snippets) this.setupSnippets($input);
// We don't instantiate the quick actions autocomplete for note and issue/MR edit forms
$input.filter('[data-supports-quick-actions="true"]').atwho({
......@@ -360,6 +362,39 @@ class GfmAutoComplete {
});
}
setupSnippets($input) {
$input.atwho({
at: '$',
alias: 'snippets',
searchKey: 'search',
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
if (value.title != null) {
tmpl = GfmAutoComplete.Issues.template;
}
return tmpl;
},
data: GfmAutoComplete.defaultLoadingData,
// eslint-disable-next-line no-template-curly-in-string
insertTpl: '${atwho-at}${id}',
callbacks: {
...this.getDefaultCallbacks(),
beforeSave(snippets) {
return $.map(snippets, (m) => {
if (m.title == null) {
return m;
}
return {
id: m.id,
title: sanitize(m.title),
search: `${m.id} ${m.title}`,
};
});
},
},
});
}
getDefaultCallbacks() {
const fetchData = this.fetchData.bind(this);
......@@ -470,7 +505,7 @@ class GfmAutoComplete {
// The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js
const atSymbolsWithBar = Object.keys(controllers).join('|');
const atSymbolsWithBar = Object.keys(controllers).join('|').replace(/[$]/, '\\$&');
const atSymbolsWithoutBar = Object.keys(controllers).join('');
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
......@@ -497,6 +532,7 @@ GfmAutoComplete.atTypeMap = {
'~': 'labels',
'%': 'milestones',
'/': 'commands',
$: 'snippets',
};
// Emoji
......@@ -519,7 +555,7 @@ GfmAutoComplete.Labels = {
// eslint-disable-next-line no-template-curly-in-string
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
};
// Issues and MergeRequests
// Issues, MergeRequests and Snippets
GfmAutoComplete.Issues = {
// eslint-disable-next-line no-template-curly-in-string
template: '<li><small>${id}</small> ${title}</li>',
......
......@@ -11,6 +11,7 @@ export default () => {
epics: false,
milestones: false,
labels: false,
snippets: false,
});
new ZenMode(); // eslint-disable-line no-new
};
......@@ -15,5 +15,6 @@ export default (initGFM = true) => {
epics: initGFM,
milestones: initGFM,
labels: initGFM,
snippets: initGFM,
});
};
......@@ -76,6 +76,7 @@
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
snippets: this.enableAutocomplete,
});
},
beforeDestroy() {
......
......@@ -27,6 +27,10 @@ def commands
render json: @autocomplete_service.commands(target, params[:type])
end
def snippets
render json: @autocomplete_service.snippets
end
private
def load_autocomplete_service
......
......@@ -292,7 +292,8 @@ def autocomplete_data_sources(object, noteable_type)
mergeRequests: merge_requests_project_autocomplete_sources_path(object),
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
milestones: milestones_project_autocomplete_sources_path(object),
commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id])
commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
snippets: snippets_project_autocomplete_sources_path(object)
}
end
end
......@@ -29,6 +29,10 @@ def commands(noteable, type)
QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
end
def snippets
SnippetsFinder.new(current_user, project: project).execute.select([:id, :title])
end
def labels_as_hash(target)
super(target, project_id: project.id, include_ancestor_groups: true)
end
......
---
title: Add autocomplete drop down filter for project snippets
merge_request: 21458
author: Fabian Schneider
type: added
......@@ -32,6 +32,7 @@
get 'labels'
get 'milestones'
get 'commands'
get 'snippets'
end
end
......
......@@ -5,6 +5,7 @@
let(:project) { create(:project) }
let(:label) { create(:label, project: project, title: 'special+') }
let(:issue) { create(:issue, project: project) }
let!(:project_snippet) { create(:project_snippet, project: project, title: 'code snippet') }
before do
project.add_maintainer(user)
......@@ -301,6 +302,16 @@
end
end
it 'shows project snippets' do
page.within '.timeline-content-form' do
find('#note-body').native.send_keys('$')
end
page.within '.atwho-container' do
expect(page).to have_content(project_snippet.title)
end
end
private
def expect_to_wrap(should_wrap, item, note, value)
......
......@@ -174,9 +174,7 @@ def element(*arguments)
it 'returns paths for autocomplete_sources_controller' do
sources = helper.autocomplete_data_sources(project, noteable_type)
expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands])
expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands, :snippets])
sources.keys.each do |key|
expect(sources[key]).not_to be_nil
end
......
......@@ -103,7 +103,7 @@ describe('GfmAutoComplete', function () {
gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext)
);
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%'];
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
const otherFlags = ['/', ':'];
const flags = flagsUseDefaultMatcher.concat(otherFlags);
......
......@@ -133,8 +133,9 @@
# labels_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/labels(.:format) projects/autocomplete_sources#labels
# milestones_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/milestones(.:format) projects/autocomplete_sources#milestones
# commands_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/commands(.:format) projects/autocomplete_sources#commands
# snippets_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/snippets(.:format) projects/autocomplete_sources#snippets
describe Projects::AutocompleteSourcesController, 'routing' do
[:members, :issues, :merge_requests, :labels, :milestones, :commands].each do |action|
[:members, :issues, :merge_requests, :labels, :milestones, :commands, :snippets].each do |action|
it "to ##{action}" do
expect(get("/gitlab/gitlabhq/autocomplete_sources/#{action}")).to route_to("projects/autocomplete_sources##{action}", namespace_id: 'gitlab', project_id: 'gitlabhq')
end
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment