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
07a8c543
Commit
07a8c543
authored
Oct 23, 2017
by
Jacob Schatz
Committed by
Filipa Lacerda
Oct 23, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Create new branch from dropdown.
parent
3f9022cf
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
449 additions
and
9 deletions
+449
-9
app/assets/javascripts/repo/components/new_branch_form.vue
app/assets/javascripts/repo/components/new_branch_form.vue
+115
-0
app/assets/javascripts/repo/components/repo.vue
app/assets/javascripts/repo/components/repo.vue
+29
-3
app/assets/javascripts/repo/index.js
app/assets/javascripts/repo/index.js
+22
-0
app/assets/javascripts/repo/services/repo_service.js
app/assets/javascripts/repo/services/repo_service.js
+10
-0
app/assets/javascripts/repo/stores/repo_store.js
app/assets/javascripts/repo/stores/repo_store.js
+1
-0
app/views/projects/tree/_tree_header.html.haml
app/views/projects/tree/_tree_header.html.haml
+1
-1
app/views/shared/_ref_switcher.html.haml
app/views/shared/_ref_switcher.html.haml
+18
-5
spec/features/projects/ref_switcher_spec.rb
spec/features/projects/ref_switcher_spec.rb
+35
-0
spec/javascripts/repo/components/new_branch_form_spec.js
spec/javascripts/repo/components/new_branch_form_spec.js
+122
-0
spec/javascripts/repo/components/repo_spec.js
spec/javascripts/repo/components/repo_spec.js
+96
-0
No files found.
app/assets/javascripts/repo/components/new_branch_form.vue
0 → 100644
View file @
07a8c543
<
script
>
import
flash
,
{
hideFlash
}
from
'
../../flash
'
;
import
loadingIcon
from
'
../../vue_shared/components/loading_icon.vue
'
;
import
eventHub
from
'
../event_hub
'
;
export
default
{
components
:
{
loadingIcon
,
},
props
:
{
currentBranch
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
branchName
:
''
,
loading
:
false
,
};
},
computed
:
{
btnDisabled
()
{
return
this
.
loading
||
this
.
branchName
===
''
;
},
},
methods
:
{
toggleDropdown
()
{
this
.
$dropdown
.
dropdown
(
'
toggle
'
);
},
submitNewBranch
()
{
// need to query as the element is appended outside of Vue
const
flashEl
=
this
.
$refs
.
flashContainer
.
querySelector
(
'
.flash-alert
'
);
this
.
loading
=
true
;
if
(
flashEl
)
{
hideFlash
(
flashEl
,
false
);
}
eventHub
.
$emit
(
'
createNewBranch
'
,
this
.
branchName
);
},
showErrorMessage
(
message
)
{
this
.
loading
=
false
;
flash
(
message
,
'
alert
'
,
this
.
$el
);
},
createdNewBranch
(
newBranchName
)
{
this
.
loading
=
false
;
this
.
branchName
=
''
;
if
(
this
.
dropdownText
)
{
this
.
dropdownText
.
textContent
=
newBranchName
;
}
},
},
created
()
{
// Dropdown is outside of Vue instance & is controlled by Bootstrap
this
.
$dropdown
=
$
(
'
.git-revision-dropdown
'
);
// text element is outside Vue app
this
.
dropdownText
=
document
.
querySelector
(
'
.project-refs-form .dropdown-toggle-text
'
);
eventHub
.
$on
(
'
createNewBranchSuccess
'
,
this
.
createdNewBranch
);
eventHub
.
$on
(
'
createNewBranchError
'
,
this
.
showErrorMessage
);
eventHub
.
$on
(
'
toggleNewBranchDropdown
'
,
this
.
toggleDropdown
);
},
destroyed
()
{
eventHub
.
$off
(
'
createNewBranchSuccess
'
,
this
.
createdNewBranch
);
eventHub
.
$off
(
'
toggleNewBranchDropdown
'
,
this
.
toggleDropdown
);
eventHub
.
$off
(
'
createNewBranchError
'
,
this
.
showErrorMessage
);
},
};
</
script
>
<
template
>
<div>
<div
class=
"flash-container"
ref=
"flashContainer"
>
</div>
<p>
Create from:
<code>
{{
currentBranch
}}
</code>
</p>
<input
class=
"form-control js-new-branch-name"
type=
"text"
placeholder=
"Name new branch"
v-model=
"branchName"
@
keyup.enter.stop.prevent=
"submitNewBranch"
/>
<div
class=
"prepend-top-default clearfix"
>
<button
type=
"button"
class=
"btn btn-primary pull-left"
:disabled=
"btnDisabled"
@
click.stop.prevent=
"submitNewBranch"
>
<loading-icon
v-if=
"loading"
:inline=
"true"
/>
<span>
Create
</span>
</button>
<button
type=
"button"
class=
"btn btn-default pull-right"
@
click.stop.prevent=
"toggleDropdown"
>
Cancel
</button>
</div>
</div>
</
template
>
app/assets/javascripts/repo/components/repo.vue
View file @
07a8c543
...
...
@@ -8,7 +8,9 @@ import RepoMixin from '../mixins/repo_mixin';
import
PopupDialog
from
'
../../vue_shared/components/popup_dialog.vue
'
;
import
Store
from
'
../stores/repo_store
'
;
import
Helper
from
'
../helpers/repo_helper
'
;
import
Service
from
'
../services/repo_service
'
;
import
MonacoLoaderHelper
from
'
../helpers/monaco_loader_helper
'
;
import
eventHub
from
'
../event_hub
'
;
export
default
{
data
()
{
...
...
@@ -24,12 +26,19 @@ export default {
PopupDialog
,
RepoPreview
,
},
created
()
{
eventHub
.
$on
(
'
createNewBranch
'
,
this
.
createNewBranch
);
},
mounted
()
{
Helper
.
getContent
().
catch
(
Helper
.
loadingError
);
},
destroyed
()
{
eventHub
.
$off
(
'
createNewBranch
'
,
this
.
createNewBranch
);
},
methods
:
{
getCurrentLocation
()
{
return
location
.
href
;
},
toggleDialogOpen
(
toggle
)
{
this
.
dialog
.
open
=
toggle
;
},
...
...
@@ -38,8 +47,25 @@ export default {
this
.
toggleDialogOpen
(
false
);
this
.
dialog
.
status
=
status
;
},
toggleBlobView
:
Store
.
toggleBlobView
,
createNewBranch
(
branch
)
{
Service
.
createBranch
({
branch
,
ref
:
Store
.
currentBranch
,
}).
then
((
res
)
=>
{
const
newBranchName
=
res
.
data
.
name
;
const
newUrl
=
this
.
getCurrentLocation
().
replace
(
Store
.
currentBranch
,
newBranchName
);
Store
.
currentBranch
=
newBranchName
;
history
.
pushState
({
key
:
Helper
.
key
},
''
,
newUrl
);
eventHub
.
$emit
(
'
createNewBranchSuccess
'
,
newBranchName
);
eventHub
.
$emit
(
'
toggleNewBranchDropdown
'
);
}).
catch
((
err
)
=>
{
eventHub
.
$emit
(
'
createNewBranchError
'
,
err
.
response
.
data
.
message
);
});
},
},
};
</
script
>
...
...
app/assets/javascripts/repo/index.js
View file @
07a8c543
...
...
@@ -5,6 +5,7 @@ import Service from './services/repo_service';
import
Store
from
'
./stores/repo_store
'
;
import
Repo
from
'
./components/repo.vue
'
;
import
RepoEditButton
from
'
./components/repo_edit_button.vue
'
;
import
newBranchForm
from
'
./components/new_branch_form.vue
'
;
import
Translate
from
'
../vue_shared/translate
'
;
function
initDropdowns
()
{
...
...
@@ -62,6 +63,26 @@ function initRepoEditButton(el) {
});
}
function
initNewBranchForm
()
{
const
el
=
document
.
querySelector
(
'
.js-new-branch-dropdown
'
);
if
(
!
el
)
return
null
;
return
new
Vue
({
el
,
components
:
{
newBranchForm
,
},
render
(
createElement
)
{
return
createElement
(
'
new-branch-form
'
,
{
props
:
{
currentBranch
:
Store
.
currentBranch
,
},
});
},
});
}
function
initRepoBundle
()
{
const
repo
=
document
.
getElementById
(
'
repo
'
);
const
editButton
=
document
.
querySelector
(
'
.editable-mode
'
);
...
...
@@ -73,6 +94,7 @@ function initRepoBundle() {
initRepo
(
repo
);
initRepoEditButton
(
editButton
);
initNewBranchForm
();
}
$
(
initRepoBundle
);
...
...
app/assets/javascripts/repo/services/repo_service.js
View file @
07a8c543
import
axios
from
'
axios
'
;
import
csrf
from
'
../../lib/utils/csrf
'
;
import
Store
from
'
../stores/repo_store
'
;
import
Api
from
'
../../api
'
;
import
Helper
from
'
../helpers/repo_helper
'
;
axios
.
defaults
.
headers
.
common
[
csrf
.
headerKey
]
=
csrf
.
token
;
const
RepoService
=
{
url
:
''
,
options
:
{
...
...
@@ -10,6 +13,7 @@ const RepoService = {
format
:
'
json
'
,
},
},
createBranchPath
:
'
/api/:version/projects/:id/repository/branches
'
,
richExtensionRegExp
:
/md/
,
getRaw
(
url
)
{
...
...
@@ -73,6 +77,12 @@ const RepoService = {
.
then
(
this
.
commitFlash
);
},
createBranch
(
payload
)
{
const
url
=
Api
.
buildUrl
(
this
.
createBranchPath
)
.
replace
(
'
:id
'
,
Store
.
projectId
);
return
axios
.
post
(
url
,
payload
);
},
commitFlash
(
data
)
{
if
(
data
.
short_id
&&
data
.
stats
)
{
window
.
Flash
(
`Your changes have been committed. Commit
${
data
.
short_id
}
with
${
data
.
stats
.
additions
}
additions,
${
data
.
stats
.
deletions
}
deletions.`
,
'
notice
'
);
...
...
app/assets/javascripts/repo/stores/repo_store.js
View file @
07a8c543
...
...
@@ -13,6 +13,7 @@ const RepoStore = {
projectId
:
''
,
projectName
:
''
,
projectUrl
:
''
,
branchUrl
:
''
,
blobRaw
:
''
,
currentBlobView
:
'
repo-preview
'
,
openedFiles
:
[],
...
...
app/views/projects/tree/_tree_header.html.haml
View file @
07a8c543
.tree-ref-container
.tree-ref-holder
=
render
'shared/ref_switcher'
,
destination:
'tree'
,
path:
@path
=
render
'shared/ref_switcher'
,
destination:
'tree'
,
path:
@path
,
show_create:
true
-
unless
show_new_repo?
=
render
'projects/tree/old_tree_header'
...
...
app/views/shared/_ref_switcher.html.haml
View file @
07a8c543
-
show_new_branch_form
=
show_new_repo?
&&
show_create
&&
can?
(
current_user
,
:push_code
,
@project
)
-
dropdown_toggle_text
=
@ref
||
@project
.
default_branch
=
form_tag
switch_project_refs_path
(
@project
),
method: :get
,
class:
"project-refs-form"
do
=
hidden_field_tag
:destination
,
destination
...
...
@@ -7,8 +8,20 @@
=
hidden_field_tag
key
,
value
,
id:
nil
.dropdown
=
dropdown_toggle
dropdown_toggle_text
,
{
toggle:
"dropdown"
,
selected:
dropdown_toggle_text
,
ref:
@ref
,
refs_url:
refs_project_path
(
@project
),
field_name:
'ref'
,
submit_form_on_click:
true
,
visit:
true
},
{
toggle_class:
"js-project-refs-dropdown"
}
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown
{
class:
(
"dropdown-menu-align-right"
if
local_assigns
[
:align_right
])
}
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown.dropdown-menu-paging
{
class:
(
"dropdown-menu-align-right"
if
local_assigns
[
:align_right
])
}
.dropdown-page-one
=
dropdown_title
_
(
"Switch branch/tag"
)
=
dropdown_filter
_
(
"Search branches and tags"
)
=
dropdown_content
=
dropdown_loading
-
if
show_new_branch_form
=
dropdown_footer
do
%ul
.dropdown-footer-list
%li
%a
.dropdown-toggle-page
{
href:
"#"
}
Create new branch
-
if
show_new_branch_form
.dropdown-page-two
=
dropdown_title
(
"Create new branch"
,
options:
{
back:
true
})
=
dropdown_content
do
.js-new-branch-dropdown
spec/features/projects/ref_switcher_spec.rb
View file @
07a8c543
...
...
@@ -6,6 +6,7 @@ feature 'Ref switcher', :js do
before
do
project
.
team
<<
[
user
,
:master
]
page
.
driver
.
set_cookie
(
'new_repo'
,
'true'
)
sign_in
(
user
)
visit
project_tree_path
(
project
,
'master'
)
end
...
...
@@ -40,4 +41,38 @@ feature 'Ref switcher', :js do
expect
(
page
).
to
have_title
"'test'"
end
context
"create branch"
do
let
(
:input
)
{
find
(
'.js-new-branch-name'
)
}
before
do
click_button
'master'
wait_for_requests
page
.
within
'.project-refs-form'
do
find
(
".dropdown-footer-list a"
).
click
end
end
it
"shows error message for the invalid branch name"
do
input
.
set
'foo bar'
click_button
(
'Create'
)
wait_for_requests
expect
(
page
).
to
have_content
'Branch name is invalid'
end
it
"should create new branch properly"
do
input
.
set
'new-branch-name'
click_button
(
'Create'
)
wait_for_requests
expect
(
find
(
'.js-project-refs-dropdown'
)).
to
have_content
'new-branch-name'
end
it
"should create new branch by Enter key"
do
input
.
set
'new-branch-name-2'
input
.
native
.
send_keys
:enter
wait_for_requests
expect
(
find
(
'.js-project-refs-dropdown'
)).
to
have_content
'new-branch-name-2'
end
end
end
spec/javascripts/repo/components/new_branch_form_spec.js
0 → 100644
View file @
07a8c543
import
Vue
from
'
vue
'
;
import
newBranchForm
from
'
~/repo/components/new_branch_form.vue
'
;
import
eventHub
from
'
~/repo/event_hub
'
;
import
RepoStore
from
'
~/repo/stores/repo_store
'
;
import
createComponent
from
'
../../helpers/vue_mount_component_helper
'
;
describe
(
'
Multi-file editor new branch form
'
,
()
=>
{
let
vm
;
beforeEach
(()
=>
{
const
Component
=
Vue
.
extend
(
newBranchForm
);
RepoStore
.
currentBranch
=
'
master
'
;
vm
=
createComponent
(
Component
,
{
currentBranch
:
RepoStore
.
currentBranch
,
});
});
afterEach
(()
=>
{
vm
.
$destroy
();
RepoStore
.
currentBranch
=
''
;
});
describe
(
'
template
'
,
()
=>
{
it
(
'
renders submit as disabled
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn
'
).
getAttribute
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
});
it
(
'
enables the submit button when branch is not empty
'
,
(
done
)
=>
{
vm
.
branchName
=
'
testing
'
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn
'
).
getAttribute
(
'
disabled
'
)).
toBeNull
();
done
();
});
});
it
(
'
displays current branch creating from
'
,
(
done
)
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
p
'
).
textContent
.
replace
(
/
\s
+/g
,
'
'
).
trim
()).
toBe
(
'
Create from: master
'
);
done
();
});
});
});
describe
(
'
submitNewBranch
'
,
()
=>
{
it
(
'
sets to loading
'
,
()
=>
{
vm
.
submitNewBranch
();
expect
(
vm
.
loading
).
toBeTruthy
();
});
it
(
'
hides current flash element
'
,
(
done
)
=>
{
vm
.
$refs
.
flashContainer
.
innerHTML
=
'
<div class="flash-alert"></div>
'
;
vm
.
submitNewBranch
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.flash-alert
'
)).
toBeNull
();
done
();
});
});
it
(
'
emits an event with branchName
'
,
()
=>
{
spyOn
(
eventHub
,
'
$emit
'
);
vm
.
branchName
=
'
testing
'
;
vm
.
submitNewBranch
();
expect
(
eventHub
.
$emit
).
toHaveBeenCalledWith
(
'
createNewBranch
'
,
'
testing
'
);
});
});
describe
(
'
showErrorMessage
'
,
()
=>
{
it
(
'
sets loading to false
'
,
()
=>
{
vm
.
loading
=
true
;
vm
.
showErrorMessage
();
expect
(
vm
.
loading
).
toBeFalsy
();
});
it
(
'
creates flash element
'
,
()
=>
{
vm
.
showErrorMessage
(
'
error message
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.flash-alert
'
)).
not
.
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.flash-alert
'
).
textContent
.
trim
()).
toBe
(
'
error message
'
);
});
});
describe
(
'
createdNewBranch
'
,
()
=>
{
it
(
'
set loading to false
'
,
()
=>
{
vm
.
loading
=
true
;
vm
.
createdNewBranch
();
expect
(
vm
.
loading
).
toBeFalsy
();
});
it
(
'
resets branch name
'
,
()
=>
{
vm
.
branchName
=
'
testing
'
;
vm
.
createdNewBranch
();
expect
(
vm
.
branchName
).
toBe
(
''
);
});
it
(
'
sets the dropdown toggle text
'
,
()
=>
{
vm
.
dropdownText
=
document
.
createElement
(
'
span
'
);
vm
.
createdNewBranch
(
'
branch name
'
);
expect
(
vm
.
dropdownText
.
textContent
).
toBe
(
'
branch name
'
);
});
});
});
spec/javascripts/repo/components/repo_spec.js
0 → 100644
View file @
07a8c543
import
Vue
from
'
vue
'
;
import
repo
from
'
~/repo/components/repo.vue
'
;
import
RepoStore
from
'
~/repo/stores/repo_store
'
;
import
Service
from
'
~/repo/services/repo_service
'
;
import
eventHub
from
'
~/repo/event_hub
'
;
import
createComponent
from
'
../../helpers/vue_mount_component_helper
'
;
describe
(
'
repo component
'
,
()
=>
{
let
vm
;
beforeEach
(()
=>
{
const
Component
=
Vue
.
extend
(
repo
);
RepoStore
.
currentBranch
=
'
master
'
;
vm
=
createComponent
(
Component
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
RepoStore
.
currentBranch
=
''
;
});
describe
(
'
createNewBranch
'
,
()
=>
{
beforeEach
(()
=>
{
spyOn
(
history
,
'
pushState
'
);
});
describe
(
'
success
'
,
()
=>
{
beforeEach
(()
=>
{
spyOn
(
Service
,
'
createBranch
'
).
and
.
returnValue
(
Promise
.
resolve
({
data
:
{
name
:
'
test
'
,
},
}));
});
it
(
'
calls createBranch with branchName
'
,
()
=>
{
eventHub
.
$emit
(
'
createNewBranch
'
,
'
test
'
);
expect
(
Service
.
createBranch
).
toHaveBeenCalledWith
({
branch
:
'
test
'
,
ref
:
RepoStore
.
currentBranch
,
});
});
it
(
'
pushes new history state
'
,
(
done
)
=>
{
RepoStore
.
currentBranch
=
'
master
'
;
spyOn
(
vm
,
'
getCurrentLocation
'
).
and
.
returnValue
(
'
http://test.com/master
'
);
eventHub
.
$emit
(
'
createNewBranch
'
,
'
test
'
);
setTimeout
(()
=>
{
expect
(
history
.
pushState
).
toHaveBeenCalledWith
(
jasmine
.
anything
(),
''
,
'
http://test.com/test
'
);
done
();
});
});
it
(
'
updates stores currentBranch
'
,
(
done
)
=>
{
eventHub
.
$emit
(
'
createNewBranch
'
,
'
test
'
);
setTimeout
(()
=>
{
expect
(
RepoStore
.
currentBranch
).
toBe
(
'
test
'
);
done
();
});
});
});
describe
(
'
failure
'
,
()
=>
{
beforeEach
(()
=>
{
spyOn
(
Service
,
'
createBranch
'
).
and
.
returnValue
(
Promise
.
reject
({
response
:
{
data
:
{
message
:
'
test
'
,
},
},
}));
});
it
(
'
emits createNewBranchError event
'
,
(
done
)
=>
{
spyOn
(
eventHub
,
'
$emit
'
).
and
.
callThrough
();
eventHub
.
$emit
(
'
createNewBranch
'
,
'
test
'
);
setTimeout
(()
=>
{
expect
(
eventHub
.
$emit
).
toHaveBeenCalledWith
(
'
createNewBranchError
'
,
'
test
'
);
done
();
});
});
});
});
});
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