Resolve "Drop down filter for project snippets"

This commit is contained in:
Fabian Schneider 2018-10-05 09:42:38 +00:00 committed by Fatih Acet
parent 16d038da1c
commit b55c320c89
13 changed files with 72 additions and 8 deletions

View file

@ -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>',

View file

@ -11,6 +11,7 @@ export default () => {
epics: false,
milestones: false,
labels: false,
snippets: false,
});
new ZenMode(); // eslint-disable-line no-new
};

View file

@ -15,5 +15,6 @@ export default (initGFM = true) => {
epics: initGFM,
milestones: initGFM,
labels: initGFM,
snippets: initGFM,
});
};

View file

@ -76,6 +76,7 @@
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
snippets: this.enableAutocomplete,
});
},
beforeDestroy() {

View file

@ -27,6 +27,10 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
render json: @autocomplete_service.commands(target, params[:type])
end
def snippets
render json: @autocomplete_service.snippets
end
private
def load_autocomplete_service

View file

@ -292,7 +292,8 @@ module ApplicationHelper
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

View file

@ -29,6 +29,10 @@ module Projects
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

View file

@ -0,0 +1,5 @@
---
title: Add autocomplete drop down filter for project snippets
merge_request: 21458
author: Fabian Schneider
type: added

View file

@ -32,6 +32,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get 'labels'
get 'milestones'
get 'commands'
get 'snippets'
end
end

View file

@ -5,6 +5,7 @@ describe 'GFM autocomplete', :js do
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 @@ describe 'GFM autocomplete', :js do
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)

View file

@ -174,9 +174,7 @@ describe ApplicationHelper do
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

View file

@ -103,7 +103,7 @@ describe('GfmAutoComplete', function () {
gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext)
);
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%'];
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
const otherFlags = ['/', ':'];
const flags = flagsUseDefaultMatcher.concat(otherFlags);

View file

@ -133,8 +133,9 @@ describe 'project routing' do
# 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