Resolve "Drop down filter for project snippets"
This commit is contained in:
parent
16d038da1c
commit
b55c320c89
13 changed files with 72 additions and 8 deletions
|
@ -15,6 +15,7 @@ export const defaultAutocompleteConfig = {
|
||||||
epics: true,
|
epics: true,
|
||||||
milestones: true,
|
milestones: true,
|
||||||
labels: true,
|
labels: true,
|
||||||
|
snippets: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class GfmAutoComplete {
|
class GfmAutoComplete {
|
||||||
|
@ -50,6 +51,7 @@ class GfmAutoComplete {
|
||||||
if (this.enableMap.milestones) this.setupMilestones($input);
|
if (this.enableMap.milestones) this.setupMilestones($input);
|
||||||
if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
|
if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
|
||||||
if (this.enableMap.labels) this.setupLabels($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
|
// We don't instantiate the quick actions autocomplete for note and issue/MR edit forms
|
||||||
$input.filter('[data-supports-quick-actions="true"]').atwho({
|
$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() {
|
getDefaultCallbacks() {
|
||||||
const fetchData = this.fetchData.bind(this);
|
const fetchData = this.fetchData.bind(this);
|
||||||
|
|
||||||
|
@ -470,7 +505,7 @@ class GfmAutoComplete {
|
||||||
// The below is taken from At.js source
|
// 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
|
// Tweaked to commands to start without a space only if char before is a non-word character
|
||||||
// https://github.com/ichord/At.js
|
// 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 atSymbolsWithoutBar = Object.keys(controllers).join('');
|
||||||
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
|
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
|
||||||
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
||||||
|
@ -497,6 +532,7 @@ GfmAutoComplete.atTypeMap = {
|
||||||
'~': 'labels',
|
'~': 'labels',
|
||||||
'%': 'milestones',
|
'%': 'milestones',
|
||||||
'/': 'commands',
|
'/': 'commands',
|
||||||
|
$: 'snippets',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Emoji
|
// Emoji
|
||||||
|
@ -519,7 +555,7 @@ GfmAutoComplete.Labels = {
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
|
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
|
||||||
};
|
};
|
||||||
// Issues and MergeRequests
|
// Issues, MergeRequests and Snippets
|
||||||
GfmAutoComplete.Issues = {
|
GfmAutoComplete.Issues = {
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
template: '<li><small>${id}</small> ${title}</li>',
|
template: '<li><small>${id}</small> ${title}</li>',
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default () => {
|
||||||
epics: false,
|
epics: false,
|
||||||
milestones: false,
|
milestones: false,
|
||||||
labels: false,
|
labels: false,
|
||||||
|
snippets: false,
|
||||||
});
|
});
|
||||||
new ZenMode(); // eslint-disable-line no-new
|
new ZenMode(); // eslint-disable-line no-new
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,5 +15,6 @@ export default (initGFM = true) => {
|
||||||
epics: initGFM,
|
epics: initGFM,
|
||||||
milestones: initGFM,
|
milestones: initGFM,
|
||||||
labels: initGFM,
|
labels: initGFM,
|
||||||
|
snippets: initGFM,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
epics: this.enableAutocomplete,
|
epics: this.enableAutocomplete,
|
||||||
milestones: this.enableAutocomplete,
|
milestones: this.enableAutocomplete,
|
||||||
labels: this.enableAutocomplete,
|
labels: this.enableAutocomplete,
|
||||||
|
snippets: this.enableAutocomplete,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
|
|
@ -27,6 +27,10 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
|
||||||
render json: @autocomplete_service.commands(target, params[:type])
|
render json: @autocomplete_service.commands(target, params[:type])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def snippets
|
||||||
|
render json: @autocomplete_service.snippets
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def load_autocomplete_service
|
def load_autocomplete_service
|
||||||
|
|
|
@ -292,7 +292,8 @@ module ApplicationHelper
|
||||||
mergeRequests: merge_requests_project_autocomplete_sources_path(object),
|
mergeRequests: merge_requests_project_autocomplete_sources_path(object),
|
||||||
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
|
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
|
||||||
milestones: milestones_project_autocomplete_sources_path(object),
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,10 @@ module Projects
|
||||||
QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
|
QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def snippets
|
||||||
|
SnippetsFinder.new(current_user, project: project).execute.select([:id, :title])
|
||||||
|
end
|
||||||
|
|
||||||
def labels_as_hash(target)
|
def labels_as_hash(target)
|
||||||
super(target, project_id: project.id, include_ancestor_groups: true)
|
super(target, project_id: project.id, include_ancestor_groups: true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add autocomplete drop down filter for project snippets
|
||||||
|
merge_request: 21458
|
||||||
|
author: Fabian Schneider
|
||||||
|
type: added
|
|
@ -32,6 +32,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
||||||
get 'labels'
|
get 'labels'
|
||||||
get 'milestones'
|
get 'milestones'
|
||||||
get 'commands'
|
get 'commands'
|
||||||
|
get 'snippets'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ describe 'GFM autocomplete', :js do
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
let(:label) { create(:label, project: project, title: 'special+') }
|
let(:label) { create(:label, project: project, title: 'special+') }
|
||||||
let(:issue) { create(:issue, project: project) }
|
let(:issue) { create(:issue, project: project) }
|
||||||
|
let!(:project_snippet) { create(:project_snippet, project: project, title: 'code snippet') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
|
@ -301,6 +302,16 @@ describe 'GFM autocomplete', :js do
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
def expect_to_wrap(should_wrap, item, note, value)
|
def expect_to_wrap(should_wrap, item, note, value)
|
||||||
|
|
|
@ -174,9 +174,7 @@ describe ApplicationHelper do
|
||||||
|
|
||||||
it 'returns paths for autocomplete_sources_controller' do
|
it 'returns paths for autocomplete_sources_controller' do
|
||||||
sources = helper.autocomplete_data_sources(project, noteable_type)
|
sources = helper.autocomplete_data_sources(project, noteable_type)
|
||||||
|
expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands, :snippets])
|
||||||
expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands])
|
|
||||||
|
|
||||||
sources.keys.each do |key|
|
sources.keys.each do |key|
|
||||||
expect(sources[key]).not_to be_nil
|
expect(sources[key]).not_to be_nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -103,7 +103,7 @@ describe('GfmAutoComplete', function () {
|
||||||
gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext)
|
gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext)
|
||||||
);
|
);
|
||||||
|
|
||||||
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%'];
|
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
|
||||||
const otherFlags = ['/', ':'];
|
const otherFlags = ['/', ':'];
|
||||||
const flags = flagsUseDefaultMatcher.concat(otherFlags);
|
const flags = flagsUseDefaultMatcher.concat(otherFlags);
|
||||||
|
|
||||||
|
|
|
@ -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
|
# 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
|
# 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
|
# 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
|
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
|
it "to ##{action}" do
|
||||||
expect(get("/gitlab/gitlabhq/autocomplete_sources/#{action}")).to route_to("projects/autocomplete_sources##{action}", namespace_id: 'gitlab', project_id: 'gitlabhq')
|
expect(get("/gitlab/gitlabhq/autocomplete_sources/#{action}")).to route_to("projects/autocomplete_sources##{action}", namespace_id: 'gitlab', project_id: 'gitlabhq')
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue