Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
0532bff6
Commit
0532bff6
authored
Aug 07, 2017
by
Bryce Johnson
Committed by
Clement Ho
Aug 07, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Group-level new issue & MR using previously selected project
parent
5f30350c
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
251 additions
and
18 deletions
+251
-18
app/assets/javascripts/project_select.js
app/assets/javascripts/project_select.js
+8
-9
app/assets/javascripts/project_select_combo_button.js
app/assets/javascripts/project_select_combo_button.js
+85
-0
app/assets/stylesheets/framework/nav.scss
app/assets/stylesheets/framework/nav.scss
+25
-1
app/views/shared/_new_project_item_select.html.haml
app/views/shared/_new_project_item_select.html.haml
+4
-3
changelogs/unreleased/group-new-issue.yml
changelogs/unreleased/group-new-issue.yml
+4
-0
spec/features/dashboard/issues_spec.rb
spec/features/dashboard/issues_spec.rb
+12
-3
spec/features/groups/empty_states_spec.rb
spec/features/groups/empty_states_spec.rb
+2
-2
spec/javascripts/fixtures/project_select_combo_button.html.haml
...avascripts/fixtures/project_select_combo_button.html.haml
+6
-0
spec/javascripts/project_select_combo_button_spec.js
spec/javascripts/project_select_combo_button_spec.js
+105
-0
No files found.
app/assets/javascripts/project_select.js
View file @
0532bff6
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-else-return, quotes, max-len */
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-else-return, quotes, max-len */
import
Api
from
'
./api
'
;
import
Api
from
'
./api
'
;
import
ProjectSelectComboButton
from
'
./project_select_combo_button
'
;
(
function
()
{
(
function
()
{
this
.
ProjectSelect
=
(
function
()
{
this
.
ProjectSelect
=
(
function
()
{
...
@@ -58,7 +59,8 @@ import Api from './api';
...
@@ -58,7 +59,8 @@ import Api from './api';
if
(
this
.
includeGroups
)
{
if
(
this
.
includeGroups
)
{
placeholder
+=
"
or group
"
;
placeholder
+=
"
or group
"
;
}
}
return
$
(
select
).
select2
({
$
(
select
).
select2
({
placeholder
:
placeholder
,
placeholder
:
placeholder
,
minimumInputLength
:
0
,
minimumInputLength
:
0
,
query
:
(
function
(
_this
)
{
query
:
(
function
(
_this
)
{
...
@@ -96,21 +98,18 @@ import Api from './api';
...
@@ -96,21 +98,18 @@ import Api from './api';
};
};
})(
this
),
})(
this
),
id
:
function
(
project
)
{
id
:
function
(
project
)
{
return
project
.
web_url
;
return
JSON
.
stringify
({
name
:
project
.
name
,
url
:
project
.
web_url
,
});
},
},
text
:
function
(
project
)
{
text
:
function
(
project
)
{
return
project
.
name_with_namespace
||
project
.
name
;
return
project
.
name_with_namespace
||
project
.
name
;
},
},
dropdownCssClass
:
"
ajax-project-dropdown
"
dropdownCssClass
:
"
ajax-project-dropdown
"
});
});
});
$
(
'
.new-project-item-select-button
'
).
on
(
'
click
'
,
function
()
{
$
(
'
.project-item-select
'
,
this
.
parentNode
).
select2
(
'
open
'
);
});
$
(
'
.project-item-select
'
).
on
(
'
click
'
,
function
()
{
return
new
ProjectSelectComboButton
(
select
);
window
.
location
=
`
${
$
(
this
).
val
()}
/
${
this
.
dataset
.
relativePath
}
`
;
});
});
}
}
...
...
app/assets/javascripts/project_select_combo_button.js
0 → 100644
View file @
0532bff6
import
AccessorUtilities
from
'
./lib/utils/accessor
'
;
export
default
class
ProjectSelectComboButton
{
constructor
(
select
)
{
this
.
projectSelectInput
=
$
(
select
);
this
.
newItemBtn
=
$
(
'
.new-project-item-link
'
);
this
.
newItemBtnBaseText
=
this
.
newItemBtn
.
data
(
'
label
'
);
this
.
itemType
=
this
.
deriveItemTypeFromLabel
();
this
.
groupId
=
this
.
projectSelectInput
.
data
(
'
groupId
'
);
this
.
bindEvents
();
this
.
initLocalStorage
();
}
bindEvents
()
{
this
.
projectSelectInput
.
siblings
(
'
.new-project-item-select-button
'
)
.
on
(
'
click
'
,
this
.
openDropdown
);
this
.
projectSelectInput
.
on
(
'
change
'
,
()
=>
this
.
selectProject
());
}
initLocalStorage
()
{
const
localStorageIsSafe
=
AccessorUtilities
.
isLocalStorageAccessSafe
();
if
(
localStorageIsSafe
)
{
const
itemTypeKebabed
=
this
.
newItemBtnBaseText
.
toLowerCase
().
split
(
'
'
).
join
(
'
-
'
);
this
.
localStorageKey
=
[
'
group
'
,
this
.
groupId
,
itemTypeKebabed
,
'
recent-project
'
].
join
(
'
-
'
);
this
.
setBtnTextFromLocalStorage
();
}
}
openDropdown
()
{
$
(
this
).
siblings
(
'
.project-item-select
'
).
select2
(
'
open
'
);
}
selectProject
()
{
const
selectedProjectData
=
JSON
.
parse
(
this
.
projectSelectInput
.
val
());
const
projectUrl
=
`
${
selectedProjectData
.
url
}
/
${
this
.
projectSelectInput
.
data
(
'
relativePath
'
)}
`
;
const
projectName
=
selectedProjectData
.
name
;
const
projectMeta
=
{
url
:
projectUrl
,
name
:
projectName
,
};
this
.
setNewItemBtnAttributes
(
projectMeta
);
this
.
setProjectInLocalStorage
(
projectMeta
);
}
setBtnTextFromLocalStorage
()
{
const
cachedProjectData
=
this
.
getProjectFromLocalStorage
();
this
.
setNewItemBtnAttributes
(
cachedProjectData
);
}
setNewItemBtnAttributes
(
project
)
{
if
(
project
)
{
this
.
newItemBtn
.
attr
(
'
href
'
,
project
.
url
);
this
.
newItemBtn
.
text
(
`
${
this
.
newItemBtnBaseText
}
in
${
project
.
name
}
`
);
this
.
newItemBtn
.
enable
();
}
else
{
this
.
newItemBtn
.
text
(
`Select project to create
${
this
.
itemType
}
`
);
this
.
newItemBtn
.
disable
();
}
}
deriveItemTypeFromLabel
()
{
// label is either 'New issue' or 'New merge request'
return
this
.
newItemBtnBaseText
.
split
(
'
'
).
slice
(
1
).
join
(
'
'
);
}
getProjectFromLocalStorage
()
{
const
projectString
=
localStorage
.
getItem
(
this
.
localStorageKey
);
return
JSON
.
parse
(
projectString
);
}
setProjectInLocalStorage
(
projectMeta
)
{
const
projectString
=
JSON
.
stringify
(
projectMeta
);
localStorage
.
setItem
(
this
.
localStorageKey
,
projectString
);
}
}
app/assets/stylesheets/framework/nav.scss
View file @
0532bff6
...
@@ -251,7 +251,6 @@
...
@@ -251,7 +251,6 @@
// Applies on /dashboard/issues
// Applies on /dashboard/issues
.project-item-select-holder
{
.project-item-select-holder
{
display
:
block
;
margin
:
0
;
margin
:
0
;
}
}
}
}
...
@@ -283,6 +282,31 @@
...
@@ -283,6 +282,31 @@
}
}
}
}
.project-item-select-holder.btn-group
{
display
:
flex
;
max-width
:
350px
;
overflow
:
hidden
;
@media
(
max-width
:
$screen-xs-max
)
{
width
:
100%
;
max-width
:
none
;
}
.new-project-item-link
{
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
.new-project-item-select-button
{
width
:
32px
;
}
}
.new-project-item-select-button
.fa-caret-down
{
margin-left
:
2px
;
}
.layout-nav
{
.layout-nav
{
width
:
100%
;
width
:
100%
;
background
:
$gray-light
;
background
:
$gray-light
;
...
...
app/views/shared/_new_project_item_select.html.haml
View file @
0532bff6
-
if
any_projects?
(
@projects
)
-
if
any_projects?
(
@projects
)
.project-item-select-holder
.project-item-select-holder.btn-group.pull-right
%a
.btn.btn-new.new-project-item-link
{
href:
''
,
data:
{
label:
local_assigns
[
:label
]
}
}
=
icon
(
'spinner spin'
)
=
project_select_tag
:project_path
,
class:
"project-item-select"
,
data:
{
include_groups:
local_assigns
[
:include_groups
],
order_by:
'last_activity_at'
,
relative_path:
local_assigns
[
:path
]
},
with_feature_enabled:
local_assigns
[
:with_feature_enabled
]
=
project_select_tag
:project_path
,
class:
"project-item-select"
,
data:
{
include_groups:
local_assigns
[
:include_groups
],
order_by:
'last_activity_at'
,
relative_path:
local_assigns
[
:path
]
},
with_feature_enabled:
local_assigns
[
:with_feature_enabled
]
%a
.btn.btn-new.new-project-item-select-button
%button
.btn.btn-new.new-project-item-select-button
=
local_assigns
[
:label
]
=
icon
(
'caret-down'
)
=
icon
(
'caret-down'
)
changelogs/unreleased/group-new-issue.yml
0 → 100644
View file @
0532bff6
---
title
:
Cache recent projects for group-level new resource creation.
merge_request
:
!13058
author
:
spec/features/dashboard/issues_spec.rb
View file @
0532bff6
...
@@ -79,12 +79,21 @@ RSpec.describe 'Dashboard Issues' do
...
@@ -79,12 +79,21 @@ RSpec.describe 'Dashboard Issues' do
end
end
end
end
it
'shows the new issue page'
,
:js
do
it
'shows the new issue page'
,
js:
true
do
find
(
'.new-project-item-select-button'
).
trigger
(
'click'
)
find
(
'.new-project-item-select-button'
).
trigger
(
'click'
)
wait_for_requests
wait_for_requests
find
(
'.select2-results li'
).
click
expect
(
page
).
to
have_current_path
(
"/
#{
project
.
path_with_namespace
}
/issues/new"
)
project_path
=
"/
#{
project
.
path_with_namespace
}
"
project_json
=
{
name:
project
.
name_with_namespace
,
url:
project_path
}.
to_json
# similate selection, and prevent overlap by dropdown menu
execute_script
(
"$('.project-item-select').val('
#{
project_json
}
').trigger('change');"
)
execute_script
(
"$('#select2-drop-mask').remove();"
)
find
(
'.new-project-item-link'
).
trigger
(
'click'
)
expect
(
page
).
to
have_current_path
(
"
#{
project_path
}
/issues/new"
)
page
.
within
(
'#content-body'
)
do
page
.
within
(
'#content-body'
)
do
expect
(
page
).
to
have_selector
(
'.issue-form'
)
expect
(
page
).
to
have_selector
(
'.issue-form'
)
...
...
spec/features/groups/empty_states_spec.rb
View file @
0532bff6
...
@@ -38,7 +38,7 @@ feature 'Groups Merge Requests Empty States' do
...
@@ -38,7 +38,7 @@ feature 'Groups Merge Requests Empty States' do
it
'should show a new merge request button'
do
it
'should show a new merge request button'
do
within
'.empty-state'
do
within
'.empty-state'
do
expect
(
page
).
to
have_content
(
'
New
merge request'
)
expect
(
page
).
to
have_content
(
'
create
merge request'
)
end
end
end
end
...
@@ -63,7 +63,7 @@ feature 'Groups Merge Requests Empty States' do
...
@@ -63,7 +63,7 @@ feature 'Groups Merge Requests Empty States' do
it
'should not show a new merge request button'
do
it
'should not show a new merge request button'
do
within
'.empty-state'
do
within
'.empty-state'
do
expect
(
page
).
not_to
have_link
(
'
New
merge request'
)
expect
(
page
).
not_to
have_link
(
'
create
merge request'
)
end
end
end
end
end
end
...
...
spec/javascripts/fixtures/project_select_combo_button.html.haml
0 → 100644
View file @
0532bff6
.project-item-select-holder
%input
.project-item-select
{
data:
{
group_id:
'12345'
,
relative_path:
'issues/new'
}
}
%a
.new-project-item-link
{
data:
{
label:
'New issue'
},
href:
''
}
%i
.fa.fa-spinner.spin
%a
.new-project-item-select-button
%i
.fa.fa-caret-down
spec/javascripts/project_select_combo_button_spec.js
0 → 100644
View file @
0532bff6
import
ProjectSelectComboButton
from
'
~/project_select_combo_button
'
;
const
fixturePath
=
'
static/project_select_combo_button.html.raw
'
;
describe
(
'
Project Select Combo Button
'
,
function
()
{
preloadFixtures
(
fixturePath
);
beforeEach
(
function
()
{
this
.
defaults
=
{
label
:
'
Select project to create issue
'
,
groupId
:
12345
,
projectMeta
:
{
name
:
'
My Cool Project
'
,
url
:
'
http://mycoolproject.com
'
,
},
newProjectMeta
:
{
name
:
'
My Other Cool Project
'
,
url
:
'
http://myothercoolproject.com
'
,
},
localStorageKey
:
'
group-12345-new-issue-recent-project
'
,
relativePath
:
'
issues/new
'
,
};
loadFixtures
(
fixturePath
);
this
.
newItemBtn
=
document
.
querySelector
(
'
.new-project-item-link
'
);
this
.
projectSelectInput
=
document
.
querySelector
(
'
.project-item-select
'
);
});
describe
(
'
on page load when localStorage is empty
'
,
function
()
{
beforeEach
(
function
()
{
this
.
comboButton
=
new
ProjectSelectComboButton
(
this
.
projectSelectInput
);
});
it
(
'
newItemBtn is disabled
'
,
function
()
{
expect
(
this
.
newItemBtn
.
hasAttribute
(
'
disabled
'
)).
toBe
(
true
);
expect
(
this
.
newItemBtn
.
classList
.
contains
(
'
disabled
'
)).
toBe
(
true
);
});
it
(
'
newItemBtn href is null
'
,
function
()
{
expect
(
this
.
newItemBtn
.
getAttribute
(
'
href
'
)).
toBe
(
''
);
});
it
(
'
newItemBtn text is the plain default label
'
,
function
()
{
expect
(
this
.
newItemBtn
.
textContent
).
toBe
(
this
.
defaults
.
label
);
});
});
describe
(
'
on page load when localStorage is filled
'
,
function
()
{
beforeEach
(
function
()
{
window
.
localStorage
.
setItem
(
this
.
defaults
.
localStorageKey
,
JSON
.
stringify
(
this
.
defaults
.
projectMeta
));
this
.
comboButton
=
new
ProjectSelectComboButton
(
this
.
projectSelectInput
);
});
it
(
'
newItemBtn is not disabled
'
,
function
()
{
expect
(
this
.
newItemBtn
.
hasAttribute
(
'
disabled
'
)).
toBe
(
false
);
expect
(
this
.
newItemBtn
.
classList
.
contains
(
'
disabled
'
)).
toBe
(
false
);
});
it
(
'
newItemBtn href is correctly set
'
,
function
()
{
expect
(
this
.
newItemBtn
.
getAttribute
(
'
href
'
)).
toBe
(
this
.
defaults
.
projectMeta
.
url
);
});
it
(
'
newItemBtn text is the cached label
'
,
function
()
{
expect
(
this
.
newItemBtn
.
textContent
)
.
toBe
(
`New issue in
${
this
.
defaults
.
projectMeta
.
name
}
`
);
});
afterEach
(
function
()
{
window
.
localStorage
.
clear
();
});
});
describe
(
'
after selecting a new project
'
,
function
()
{
beforeEach
(
function
()
{
this
.
comboButton
=
new
ProjectSelectComboButton
(
this
.
projectSelectInput
);
// mock the effect of selecting an item from the projects dropdown (select2)
$
(
'
.project-item-select
'
)
.
val
(
JSON
.
stringify
(
this
.
defaults
.
newProjectMeta
))
.
trigger
(
'
change
'
);
});
it
(
'
newItemBtn is not disabled
'
,
function
()
{
expect
(
this
.
newItemBtn
.
hasAttribute
(
'
disabled
'
)).
toBe
(
false
);
expect
(
this
.
newItemBtn
.
classList
.
contains
(
'
disabled
'
)).
toBe
(
false
);
});
it
(
'
newItemBtn href is correctly set
'
,
function
()
{
expect
(
this
.
newItemBtn
.
getAttribute
(
'
href
'
))
.
toBe
(
'
http://myothercoolproject.com/issues/new
'
);
});
it
(
'
newItemBtn text is the selected project label
'
,
function
()
{
expect
(
this
.
newItemBtn
.
textContent
)
.
toBe
(
`New issue in
${
this
.
defaults
.
newProjectMeta
.
name
}
`
);
});
afterEach
(
function
()
{
window
.
localStorage
.
clear
();
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment