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
1
Merge Requests
1
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
nexedi
gitlab-ce
Commits
be74e393
Commit
be74e393
authored
Jul 10, 2018
by
Phil Hughes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improvements to new entry dropdowns in Web IDE
Closes #44845
parent
e68a547b
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
357 additions
and
247 deletions
+357
-247
app/assets/javascripts/ide/components/ide.vue
app/assets/javascripts/ide/components/ide.vue
+3
-0
app/assets/javascripts/ide/components/ide_tree.vue
app/assets/javascripts/ide/components/ide_tree.vue
+30
-7
app/assets/javascripts/ide/components/new_dropdown/button.vue
...assets/javascripts/ide/components/new_dropdown/button.vue
+51
-0
app/assets/javascripts/ide/components/new_dropdown/index.vue
app/assets/javascripts/ide/components/new_dropdown/index.vue
+31
-39
app/assets/javascripts/ide/components/new_dropdown/modal.vue
app/assets/javascripts/ide/components/new_dropdown/modal.vue
+32
-40
app/assets/javascripts/ide/components/new_dropdown/upload.vue
...assets/javascripts/ide/components/new_dropdown/upload.vue
+69
-55
app/assets/javascripts/ide/components/repo_file.vue
app/assets/javascripts/ide/components/repo_file.vue
+11
-0
app/assets/javascripts/ide/stores/actions.js
app/assets/javascripts/ide/stores/actions.js
+10
-3
app/assets/javascripts/ide/stores/mutation_types.js
app/assets/javascripts/ide/stores/mutation_types.js
+2
-0
app/assets/javascripts/ide/stores/mutations.js
app/assets/javascripts/ide/stores/mutations.js
+5
-0
app/assets/javascripts/ide/stores/state.js
app/assets/javascripts/ide/stores/state.js
+4
-0
app/assets/javascripts/vue_shared/components/gl_modal.vue
app/assets/javascripts/vue_shared/components/gl_modal.vue
+6
-0
app/assets/stylesheets/pages/repo.scss
app/assets/stylesheets/pages/repo.scss
+22
-26
changelogs/unreleased/ide-row-dropdown-design-update.yml
changelogs/unreleased/ide-row-dropdown-design-update.yml
+5
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/features/projects/tree/create_directory_spec.rb
spec/features/projects/tree/create_directory_spec.rb
+2
-6
spec/features/projects/tree/create_file_spec.rb
spec/features/projects/tree/create_file_spec.rb
+1
-3
spec/features/projects/tree/upload_file_spec.rb
spec/features/projects/tree/upload_file_spec.rb
+0
-4
spec/javascripts/ide/components/new_dropdown/button_spec.js
spec/javascripts/ide/components/new_dropdown/button_spec.js
+49
-0
spec/javascripts/ide/components/new_dropdown/index_spec.js
spec/javascripts/ide/components/new_dropdown/index_spec.js
+11
-35
spec/javascripts/ide/components/new_dropdown/modal_spec.js
spec/javascripts/ide/components/new_dropdown/modal_spec.js
+10
-26
spec/javascripts/ide/components/new_dropdown/upload_spec.js
spec/javascripts/ide/components/new_dropdown/upload_spec.js
+0
-3
No files found.
app/assets/javascripts/ide/components/ide.vue
View file @
be74e393
<
script
>
import
Mousetrap
from
'
mousetrap
'
;
import
{
mapActions
,
mapState
,
mapGetters
}
from
'
vuex
'
;
import
NewModal
from
'
./new_dropdown/modal.vue
'
;
import
IdeSidebar
from
'
./ide_side_bar.vue
'
;
import
RepoTabs
from
'
./repo_tabs.vue
'
;
import
IdeStatusBar
from
'
./ide_status_bar.vue
'
;
...
...
@@ -13,6 +14,7 @@ const originalStopCallback = Mousetrap.stopCallback;
export
default
{
components
:
{
NewModal
,
IdeSidebar
,
RepoTabs
,
IdeStatusBar
,
...
...
@@ -137,5 +139,6 @@ export default {
/>
</div>
<ide-status-bar
:file=
"activeFile"
/>
<new-modal
/>
</article>
</template>
app/assets/javascripts/ide/components/ide_tree.vue
View file @
be74e393
<
script
>
import
{
mapState
,
mapGetters
,
mapActions
}
from
'
vuex
'
;
import
NewDropdown
from
'
./new_dropdown/index
.vue
'
;
import
Icon
from
'
~/vue_shared/components/icon
.vue
'
;
import
IdeTreeList
from
'
./ide_tree_list.vue
'
;
import
Upload
from
'
./new_dropdown/upload.vue
'
;
import
NewEntryButton
from
'
./new_dropdown/button.vue
'
;
export
default
{
components
:
{
NewDropdown
,
Icon
,
Upload
,
IdeTreeList
,
NewEntryButton
,
},
computed
:
{
...
mapState
([
'
currentBranchId
'
]),
...
...
@@ -20,23 +24,42 @@ export default {
}
},
methods
:
{
...
mapActions
([
'
updateViewer
'
]),
...
mapActions
([
'
updateViewer
'
,
'
openNewEntryModal
'
,
'
createTempEntry
'
]),
},
};
</
script
>
<
template
>
<ide-tree-list
header-class=
"d-flex w-100"
viewer-type=
"editor"
>
<template
slot=
"header"
>
{{
__
(
'
Edit
'
)
}}
<new-dropdown
:project-id=
"currentProject.name_with_namespace"
:branch=
"currentBranchId"
/>
<div
class=
"ml-auto d-flex"
>
<new-entry-button
:label=
"__('New file')"
:show-label=
"false"
class=
"d-flex border-0 p-0 mr-3"
icon=
"doc-new"
@
click=
"openNewEntryModal(
{ type: 'blob' })"
/>
<upload
:show-label=
"false"
class=
"d-flex mr-3"
button-css-classes=
"border-0 p-0"
@
create=
"createTempEntry"
/>
<new-entry-button
:label=
"__('New directory')"
:show-label=
"false"
class=
"d-flex border-0 p-0"
icon=
"folder-new"
@
click=
"openNewEntryModal(
{ type: 'tree' })"
/>
</div>
</
template
>
</ide-tree-list>
</template>
app/assets/javascripts/ide/components/new_dropdown/button.vue
0 → 100644
View file @
be74e393
<
script
>
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
export
default
{
components
:
{
Icon
,
},
props
:
{
label
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
icon
:
{
type
:
String
,
required
:
true
,
},
iconClasses
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
showLabel
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
},
methods
:
{
clicked
()
{
this
.
$emit
(
'
click
'
);
},
},
};
</
script
>
<
template
>
<button
:aria-label=
"label"
type=
"button"
@
click.stop.prevent=
"clicked"
>
<icon
:name=
"icon"
:css-classes=
"iconClasses"
/>
<template
v-if=
"showLabel"
>
{{
label
}}
</
template
>
</button>
</template>
app/assets/javascripts/ide/components/new_dropdown/index.vue
View file @
be74e393
...
...
@@ -3,12 +3,14 @@ import { mapActions } from 'vuex';
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
newModal
from
'
./modal.vue
'
;
import
upload
from
'
./upload.vue
'
;
import
ItemButton
from
'
./button.vue
'
;
export
default
{
components
:
{
icon
,
newModal
,
upload
,
ItemButton
,
},
props
:
{
branch
:
{
...
...
@@ -20,11 +22,13 @@ export default {
required
:
false
,
default
:
''
,
},
mouseOver
:
{
type
:
Boolean
,
required
:
true
,
},
},
data
()
{
return
{
openModal
:
false
,
modalType
:
''
,
dropdownOpen
:
false
,
};
},
...
...
@@ -34,17 +38,18 @@ export default {
this
.
$refs
.
dropdownMenu
.
scrollIntoView
();
});
},
mouseOver
()
{
if
(
!
this
.
mouseOver
)
{
this
.
dropdownOpen
=
false
;
}
},
},
methods
:
{
...
mapActions
([
'
createTempEntry
'
]),
...
mapActions
([
'
createTempEntry
'
,
'
openNewEntryModal
'
]),
createNewItem
(
type
)
{
this
.
modalType
=
type
;
this
.
openModal
=
true
;
this
.
openNewEntryModal
({
type
,
path
:
this
.
path
});
this
.
dropdownOpen
=
false
;
},
hideModal
()
{
this
.
openModal
=
false
;
},
openDropdown
()
{
this
.
dropdownOpen
=
!
this
.
dropdownOpen
;
},
...
...
@@ -58,23 +63,19 @@ export default {
:class=
"
{
show: dropdownOpen,
}"
class="dropdown"
class="dropdown
d-flex
"
>
<button
:aria-label=
"__('Create new file or directory')"
type=
"button"
class=
"btn btn-sm btn-default dropdown-toggle add-to-tree"
aria-label=
"Create new file or directory"
class=
"rounded border-0 d-flex ide-entry-dropdown-toggle"
@
click.stop=
"openDropdown()"
>
<icon
:size=
"12"
name=
"plus"
css-classes=
"float-left"
name=
"hamburger"
/>
<icon
:size=
"12"
name=
"arrow-down"
css-classes=
"float-left"
/>
</button>
<ul
...
...
@@ -82,39 +83,30 @@ export default {
class=
"dropdown-menu dropdown-menu-right"
>
<li>
<
a
href=
"#
"
role=
"button
"
@
click.stop.prevent=
"createNewItem('blob')
"
>
{{
__
(
'
New file
'
)
}}
</a
>
<
item-button
:label=
"__('New file')
"
class=
"d-flex
"
icon=
"doc-new
"
icon-classes=
"mr-2"
@
click=
"createNewItem('blob')"
/
>
</li>
<li>
<upload
:branch-id=
"branch"
:path=
"path"
@
create=
"createTempEntry"
/>
</li>
<li>
<
a
href=
"#
"
role=
"button
"
@
click.stop.prevent=
"createNewItem('tree')
"
>
{{
__
(
'
New directory
'
)
}}
</a
>
<
item-button
:label=
"__('New directory')
"
class=
"d-flex
"
icon=
"folder-new
"
icon-classes=
"mr-2"
@
click=
"createNewItem('tree')"
/
>
</li>
</ul>
</div>
<new-modal
v-if=
"openModal"
:type=
"modalType"
:branch-id=
"branch"
:path=
"path"
@
hide=
"hideModal"
@
create=
"createTempEntry"
/>
</div>
</
template
>
app/assets/javascripts/ide/components/new_dropdown/modal.vue
View file @
be74e393
<
script
>
import
{
__
}
from
'
~/locale
'
;
import
DeprecatedModal
from
'
~/vue_shared/components/deprecated_modal.vue
'
;
import
{
mapActions
,
mapState
}
from
'
vuex
'
;
import
GlModal
from
'
~/vue_shared/components/gl_modal.vue
'
;
export
default
{
components
:
{
DeprecatedModal
,
},
props
:
{
branchId
:
{
type
:
String
,
required
:
true
,
},
type
:
{
type
:
String
,
required
:
true
,
},
path
:
{
type
:
String
,
required
:
true
,
},
GlModal
,
},
data
()
{
return
{
entryName
:
this
.
path
!==
''
?
`
${
this
.
path
}
/`
:
''
,
name
:
''
,
};
},
computed
:
{
...
mapState
([
'
newEntryModal
'
]),
entryName
:
{
get
()
{
return
this
.
name
||
(
this
.
newEntryModal
.
path
!==
''
?
`
${
this
.
newEntryModal
.
path
}
/`
:
''
);
},
set
(
val
)
{
this
.
name
=
val
;
},
},
modalTitle
()
{
if
(
this
.
type
===
'
tree
'
)
{
if
(
this
.
newEntryModal
.
type
===
'
tree
'
)
{
return
__
(
'
Create new directory
'
);
}
return
__
(
'
Create new file
'
);
},
buttonLabel
()
{
if
(
this
.
type
===
'
tree
'
)
{
if
(
this
.
newEntryModal
.
type
===
'
tree
'
)
{
return
__
(
'
Create directory
'
);
}
return
__
(
'
Create file
'
);
},
},
mounted
()
{
this
.
$refs
.
fieldName
.
focus
();
},
methods
:
{
...
mapActions
([
'
createTempEntry
'
]),
createEntryInStore
()
{
this
.
$emit
(
'
create
'
,
{
branchId
:
this
.
branchId
,
name
:
this
.
entryName
,
type
:
this
.
type
,
this
.
createTempEntry
({
name
:
this
.
name
,
type
:
this
.
newEntryModal
.
type
,
});
this
.
hideModal
();
},
hideModal
()
{
this
.
$emit
(
'
hide
'
);
focusInput
()
{
setTimeout
(()
=>
{
this
.
$refs
.
fieldName
.
focus
();
});
},
},
};
</
script
>
<
template
>
<
deprecated
-modal
:title=
"modalTitle
"
:
primary-button-label=
"buttonLabel
"
kind=
"success
"
@
cancel=
"hideModal
"
<
gl
-modal
id=
"ide-new-entry
"
:
header-title-text=
"modalTitle
"
:footer-primary-button-text=
"buttonLabel
"
footer-primary-button-variant=
"success
"
@
submit=
"createEntryInStore"
@
open=
"focusInput"
>
<form
slot=
"body"
<div
class=
"form-group row"
@
submit.prevent=
"createEntryInStore"
>
<label
class=
"label-light col-form-label col-sm-3"
>
{{
__
(
'
Name
'
)
}}
...
...
@@ -85,6 +77,6 @@ export default {
class=
"form-control"
/>
</div>
</
form
>
</
deprecated
-modal>
</
div
>
</
gl
-modal>
</
template
>
app/assets/javascripts/ide/components/new_dropdown/upload.vue
View file @
be74e393
<
script
>
export
default
{
props
:
{
branchId
:
{
type
:
String
,
required
:
true
,
},
path
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
ItemButton
from
'
./button.vue
'
;
export
default
{
components
:
{
Icon
,
ItemButton
,
},
props
:
{
path
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
mounted
()
{
this
.
$refs
.
fileUpload
.
addEventListener
(
'
change
'
,
this
.
openFile
);
showLabel
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
beforeDestroy
()
{
this
.
$refs
.
fileUpload
.
removeEventListener
(
'
change
'
,
this
.
openFile
);
buttonCssClasses
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
methods
:
{
createFile
(
target
,
file
,
isText
)
{
const
{
name
}
=
file
;
let
{
result
}
=
target
;
},
mounted
()
{
this
.
$refs
.
fileUpload
.
addEventListener
(
'
change
'
,
this
.
openFile
);
},
beforeDestroy
()
{
this
.
$refs
.
fileUpload
.
removeEventListener
(
'
change
'
,
this
.
openFile
);
},
methods
:
{
createFile
(
target
,
file
,
isText
)
{
const
{
name
}
=
file
;
let
{
result
}
=
target
;
if
(
!
isText
)
{
// eslint-disable-next-line prefer-destructuring
result
=
result
.
split
(
'
base64,
'
)[
1
];
}
if
(
!
isText
)
{
// eslint-disable-next-line prefer-destructuring
result
=
result
.
split
(
'
base64,
'
)[
1
];
}
this
.
$emit
(
'
create
'
,
{
name
:
`
${(
this
.
path
?
`
${
this
.
path
}
/`
:
''
)}${
name
}
`
,
branchId
:
this
.
branchId
,
type
:
'
blob
'
,
content
:
result
,
base64
:
!
isText
,
});
},
readFile
(
file
)
{
const
reader
=
new
FileReader
();
const
isText
=
file
.
type
.
match
(
/text.*/
)
!==
null
;
this
.
$emit
(
'
create
'
,
{
name
:
`
${
this
.
path
?
`
${
this
.
path
}
/`
:
''
}${
name
}
`
,
type
:
'
blob
'
,
content
:
result
,
base64
:
!
isText
,
});
},
readFile
(
file
)
{
const
reader
=
new
FileReader
();
const
isText
=
file
.
type
.
match
(
/text.*/
)
!==
null
;
reader
.
addEventListener
(
'
load
'
,
e
=>
this
.
createFile
(
e
.
target
,
file
,
isText
),
{
once
:
true
});
reader
.
addEventListener
(
'
load
'
,
e
=>
this
.
createFile
(
e
.
target
,
file
,
isText
),
{
once
:
true
});
if
(
isText
)
{
reader
.
readAsText
(
file
);
}
else
{
reader
.
readAsDataURL
(
file
);
}
},
openFile
()
{
Array
.
from
(
this
.
$refs
.
fileUpload
.
files
).
forEach
(
file
=>
this
.
readFile
(
file
));
},
startFileUpload
()
{
this
.
$refs
.
fileUpload
.
click
();
},
if
(
isText
)
{
reader
.
readAsText
(
file
);
}
else
{
reader
.
readAsDataURL
(
file
);
}
},
openFile
()
{
Array
.
from
(
this
.
$refs
.
fileUpload
.
files
).
forEach
(
file
=>
this
.
readFile
(
file
));
},
};
startFileUpload
()
{
this
.
$refs
.
fileUpload
.
click
();
},
},
};
</
script
>
<
template
>
<div>
<a
href=
"#"
role=
"button"
@
click.stop.prevent=
"startFileUpload"
>
{{
__
(
'
Upload file
'
)
}}
</a>
<item-button
:class=
"buttonCssClasses"
:show-label=
"showLabel"
:icon-classes=
"showLabel ? 'mr-2' : ''"
:label=
"__('Upload file')"
class=
"d-flex"
icon=
"upload"
@
click=
"startFileUpload"
/>
<input
id=
"file-upload"
ref=
"fileUpload"
...
...
app/assets/javascripts/ide/components/repo_file.vue
View file @
be74e393
...
...
@@ -40,6 +40,11 @@ export default {
default
:
false
,
},
},
data
()
{
return
{
mouseOver
:
false
,
};
},
computed
:
{
...
mapGetters
([
'
getChangesInFolder
'
,
...
...
@@ -142,6 +147,9 @@ export default {
hasUrlAtCurrentRoute
()
{
return
this
.
$router
.
currentRoute
.
path
===
`/project
${
this
.
file
.
url
}
`
;
},
toggleHover
(
over
)
{
this
.
mouseOver
=
over
;
},
},
};
</
script
>
...
...
@@ -153,6 +161,8 @@ export default {
class=
"file"
role=
"button"
@
click=
"clickFile"
@
mouseover=
"toggleHover(true)"
@
mouseout=
"toggleHover(false)"
>
<div
class=
"file-name"
...
...
@@ -206,6 +216,7 @@ export default {
:project-id=
"file.projectId"
:branch=
"file.branchId"
:path=
"file.path"
:mouse-over=
"mouseOver"
class=
"float-right prepend-left-8"
/>
</div>
...
...
app/assets/javascripts/ide/stores/actions.js
View file @
be74e393
...
...
@@ -52,7 +52,7 @@ export const setResizingStatus = ({ commit }, resizing) => {
export
const
createTempEntry
=
(
{
state
,
commit
,
dispatch
},
{
branchId
,
name
,
type
,
content
=
''
,
base64
=
false
},
{
name
,
type
,
content
=
''
,
base64
=
false
},
)
=>
new
Promise
(
resolve
=>
{
const
worker
=
new
FilesDecoratorWorker
();
...
...
@@ -81,7 +81,7 @@ export const createTempEntry = (
commit
(
types
.
CREATE_TMP_ENTRY
,
{
data
,
projectId
:
state
.
currentProjectId
,
branchId
,
branchId
:
state
.
currentBranchId
,
});
if
(
type
===
'
blob
'
)
{
...
...
@@ -100,7 +100,7 @@ export const createTempEntry = (
worker
.
postMessage
({
data
:
[
fullName
],
projectId
:
state
.
currentProjectId
,
branchId
,
branchId
:
state
.
currentBranchId
,
type
,
tempFile
:
true
,
base64
,
...
...
@@ -178,6 +178,13 @@ export const setLinks = ({ commit }, links) => commit(types.SET_LINKS, links);
export
const
setErrorMessage
=
({
commit
},
errorMessage
)
=>
commit
(
types
.
SET_ERROR_MESSAGE
,
errorMessage
);
export
const
openNewEntryModal
=
({
commit
},
{
type
,
path
=
''
})
=>
{
commit
(
types
.
OPEN_NEW_ENTRY_MODAL
,
{
type
,
path
});
// open the modal manually so we don't mess around with dropdown/rows
$
(
'
#ide-new-entry
'
).
modal
(
'
show
'
);
};
export
*
from
'
./actions/tree
'
;
export
*
from
'
./actions/file
'
;
export
*
from
'
./actions/project
'
;
...
...
app/assets/javascripts/ide/stores/mutation_types.js
View file @
be74e393
...
...
@@ -74,3 +74,5 @@ export const CLEAR_PROJECTS = 'CLEAR_PROJECTS';
export
const
RESET_OPEN_FILES
=
'
RESET_OPEN_FILES
'
;
export
const
SET_ERROR_MESSAGE
=
'
SET_ERROR_MESSAGE
'
;
export
const
OPEN_NEW_ENTRY_MODAL
=
'
OPEN_NEW_ENTRY_MODAL
'
;
app/assets/javascripts/ide/stores/mutations.js
View file @
be74e393
...
...
@@ -166,6 +166,11 @@ export default {
[
types
.
SET_ERROR_MESSAGE
](
state
,
errorMessage
)
{
Object
.
assign
(
state
,
{
errorMessage
});
},
[
types
.
OPEN_NEW_ENTRY_MODAL
](
state
,
{
type
,
path
})
{
Object
.
assign
(
state
,
{
newEntryModal
:
{
type
,
path
},
});
},
...
projectMutations
,
...
mergeRequestMutation
,
...
fileMutations
,
...
...
app/assets/javascripts/ide/stores/state.js
View file @
be74e393
...
...
@@ -26,4 +26,8 @@ export default () => ({
rightPane
:
null
,
links
:
{},
errorMessage
:
null
,
newEntryModal
:
{
type
:
''
,
path
:
''
,
},
});
app/assets/javascripts/vue_shared/components/gl_modal.vue
View file @
be74e393
...
...
@@ -45,6 +45,11 @@ export default {
emitSubmit
(
event
)
{
this
.
$emit
(
'
submit
'
,
event
);
},
opened
({
propertyName
})
{
if
(
propertyName
===
'
opacity
'
)
{
this
.
$emit
(
'
open
'
);
}
},
},
};
</
script
>
...
...
@@ -55,6 +60,7 @@ export default {
class=
"modal fade"
tabindex=
"-1"
role=
"dialog"
@
transitionend=
"opened"
>
<div
:class=
"modalSizeClass"
...
...
app/assets/stylesheets/pages/repo.scss
View file @
be74e393
...
...
@@ -44,6 +44,7 @@
padding-bottom
:
$grid-size
;
.file
{
height
:
32px
;
cursor
:
pointer
;
&
.file-active
{
...
...
@@ -716,32 +717,6 @@
justify-content
:
center
;
}
.ide-new-btn
{
.btn
{
padding-top
:
3px
;
padding-bottom
:
3px
;
}
.dropdown
{
display
:
flex
;
}
.dropdown-toggle
svg
{
top
:
0
;
}
.dropdown-menu
{
left
:
auto
;
right
:
0
;
label
{
font-weight
:
$gl-font-weight-normal
;
padding
:
5px
8px
;
margin-bottom
:
0
;
}
}
}
.ide
{
overflow
:
hidden
;
...
...
@@ -1340,3 +1315,24 @@
overflow
:
auto
;
}
}
.ide-entry-dropdown-toggle
{
padding
:
$gl-padding-4
;
background-color
:
$theme-gray-100
;
&
:hover
{
background-color
:
$theme-gray-200
;
}
&
:active
,
&
:focus
{
color
:
$white-normal
;
background-color
:
$blue-500
;
outline
:
0
;
}
}
.ide-new-btn
.dropdown.show
.ide-entry-dropdown-toggle
{
color
:
$white-normal
;
background-color
:
$blue-500
;
}
changelogs/unreleased/ide-row-dropdown-design-update.yml
0 → 100644
View file @
be74e393
---
title
:
Updated design of new entry dropdown in Web IDE
merge_request
:
20526
author
:
type
:
changed
locale/gitlab.pot
View file @
be74e393
...
...
@@ -1720,6 +1720,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
msgid "Create new file or directory"
msgstr ""
msgid "Create new label"
msgstr ""
...
...
spec/features/projects/tree/create_directory_spec.rb
View file @
be74e393
...
...
@@ -22,9 +22,7 @@ describe 'Multi-file editor new directory', :js do
end
it
'creates directory in current directory'
do
find
(
'.add-to-tree'
).
click
click_link
(
'New directory'
)
all
(
'.ide-tree-header button'
).
last
.
click
page
.
within
(
'.modal'
)
do
find
(
'.form-control'
).
set
(
'folder name'
)
...
...
@@ -32,9 +30,7 @@ describe 'Multi-file editor new directory', :js do
click_button
(
'Create directory'
)
end
find
(
'.add-to-tree'
).
click
click_link
(
'New file'
)
first
(
'.ide-tree-header button'
).
click
page
.
within
(
'.modal-dialog'
)
do
find
(
'.form-control'
).
set
(
'file name'
)
...
...
spec/features/projects/tree/create_file_spec.rb
View file @
be74e393
...
...
@@ -22,9 +22,7 @@ describe 'Multi-file editor new file', :js do
end
it
'creates file in current directory'
do
find
(
'.add-to-tree'
).
click
click_link
(
'New file'
)
first
(
'.ide-tree-header button'
).
click
page
.
within
(
'.modal'
)
do
find
(
'.form-control'
).
set
(
'file name'
)
...
...
spec/features/projects/tree/upload_file_spec.rb
View file @
be74e393
...
...
@@ -24,14 +24,10 @@ describe 'Multi-file editor upload file', :js do
end
it
'uploads text file'
do
find
(
'.add-to-tree'
).
click
# make the field visible so capybara can use it
execute_script
(
'document.querySelector("#file-upload").classList.remove("hidden")'
)
attach_file
(
'file-upload'
,
txt_file
)
find
(
'.add-to-tree'
).
click
expect
(
page
).
to
have_selector
(
'.multi-file-tab'
,
text:
'doc_sample.txt'
)
expect
(
find
(
'.blob-editor-container .lines-content'
)[
'innerText'
]).
to
have_content
(
File
.
open
(
txt_file
,
&
:readline
))
end
...
...
spec/javascripts/ide/components/new_dropdown/button_spec.js
0 → 100644
View file @
be74e393
import
Vue
from
'
vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
Button
from
'
~/ide/components/new_dropdown/button.vue
'
;
describe
(
'
IDE new entry dropdown button component
'
,
()
=>
{
let
Component
;
let
vm
;
beforeAll
(()
=>
{
Component
=
Vue
.
extend
(
Button
);
});
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
label
:
'
Testing
'
,
icon
:
'
doc-new
'
,
});
spyOn
(
vm
,
'
$emit
'
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
});
it
(
'
renders button with label
'
,
()
=>
{
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
Testing
'
);
});
it
(
'
renders icon
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.ic-doc-new
'
)).
not
.
toBe
(
null
);
});
it
(
'
emits click event
'
,
()
=>
{
vm
.
$el
.
click
();
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
click
'
);
});
it
(
'
hides label if showLabel is false
'
,
done
=>
{
vm
.
showLabel
=
false
;
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
textContent
).
not
.
toContain
(
'
Testing
'
);
done
();
});
});
});
spec/javascripts/ide/components/new_dropdown/index_spec.js
View file @
be74e393
...
...
@@ -13,6 +13,7 @@ describe('new dropdown component', () => {
vm
=
createComponentWithStore
(
component
,
store
,
{
branch
:
'
master
'
,
path
:
''
,
mouseOver
:
false
,
});
vm
.
$store
.
state
.
currentProjectId
=
'
abcproject
'
;
...
...
@@ -21,6 +22,8 @@ describe('new dropdown component', () => {
tree
:
[],
};
spyOn
(
vm
,
'
openNewEntryModal
'
);
vm
.
$mount
();
});
...
...
@@ -31,50 +34,23 @@ describe('new dropdown component', () => {
});
it
(
'
renders new file, upload and new directory links
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
a
'
)[
0
].
textContent
.
trim
()).
toBe
(
'
New file
'
);
expect
(
vm
.
$el
.
querySelectorAll
(
'
a
'
)[
1
].
textContent
.
trim
()).
toBe
(
'
Upload file
'
);
expect
(
vm
.
$el
.
querySelectorAll
(
'
a
'
)[
2
].
textContent
.
trim
()).
toBe
(
'
New directory
'
);
const
buttons
=
vm
.
$el
.
querySelectorAll
(
'
.dropdown-menu button
'
);
expect
(
buttons
[
0
].
textContent
.
trim
()).
toBe
(
'
New file
'
);
expect
(
buttons
[
1
].
textContent
.
trim
()).
toBe
(
'
Upload file
'
);
expect
(
buttons
[
2
].
textContent
.
trim
()).
toBe
(
'
New directory
'
);
});
describe
(
'
createNewItem
'
,
()
=>
{
it
(
'
sets modalType to blob when new file is clicked
'
,
()
=>
{
vm
.
$el
.
querySelectorAll
(
'
a
'
)[
0
].
click
();
vm
.
$el
.
querySelectorAll
(
'
.dropdown-menu button
'
)[
0
].
click
();
expect
(
vm
.
modalType
).
toBe
(
'
blob
'
);
expect
(
vm
.
openNewEntryModal
).
toHaveBeenCalledWith
({
type
:
'
blob
'
,
path
:
''
}
);
});
it
(
'
sets modalType to tree when new directory is clicked
'
,
()
=>
{
vm
.
$el
.
querySelectorAll
(
'
a
'
)[
2
].
click
();
expect
(
vm
.
modalType
).
toBe
(
'
tree
'
);
});
it
(
'
opens modal when link is clicked
'
,
done
=>
{
vm
.
$el
.
querySelectorAll
(
'
a
'
)[
0
].
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.modal
'
)).
not
.
toBeNull
();
done
();
});
});
});
describe
(
'
hideModal
'
,
()
=>
{
beforeAll
(
done
=>
{
vm
.
openModal
=
true
;
Vue
.
nextTick
(
done
);
});
it
(
'
closes modal after toggling
'
,
done
=>
{
vm
.
hideModal
();
vm
.
$el
.
querySelectorAll
(
'
.dropdown-menu button
'
)[
2
].
click
();
Vue
.
nextTick
()
.
then
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.modal
'
)).
toBeNull
();
})
.
then
(
done
)
.
catch
(
done
.
fail
);
expect
(
vm
.
openNewEntryModal
).
toHaveBeenCalledWith
({
type
:
'
tree
'
,
path
:
''
});
});
});
...
...
spec/javascripts/ide/components/new_dropdown/modal_spec.js
View file @
be74e393
import
Vue
from
'
vue
'
;
import
{
createStore
}
from
'
~/ide/stores
'
;
import
modal
from
'
~/ide/components/new_dropdown/modal.vue
'
;
import
createComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
createComponentWithStore
}
from
'
spec/helpers/vue_mount_component_helper
'
;
describe
(
'
new file modal component
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
modal
);
...
...
@@ -13,13 +14,15 @@ describe('new file modal component', () => {
[
'
tree
'
,
'
blob
'
].
forEach
(
type
=>
{
describe
(
type
,
()
=>
{
beforeEach
(()
=>
{
vm
=
createComponent
(
Component
,
{
const
store
=
createStore
();
store
.
state
.
newEntryModal
=
{
type
,
branchId
:
'
master
'
,
path
:
''
,
});
};
vm
=
createComponentWithStore
(
Component
,
store
).
$mount
();
vm
.
entryN
ame
=
'
testing
'
;
vm
.
n
ame
=
'
testing
'
;
});
it
(
`sets modal title as
${
type
}
`
,
()
=>
{
...
...
@@ -40,12 +43,11 @@ describe('new file modal component', () => {
describe
(
'
createEntryInStore
'
,
()
=>
{
it
(
'
$emits create
'
,
()
=>
{
spyOn
(
vm
,
'
$emit
'
);
spyOn
(
vm
,
'
createTempEntry
'
);
vm
.
createEntryInStore
();
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
create
'
,
{
branchId
:
'
master
'
,
expect
(
vm
.
createTempEntry
).
toHaveBeenCalledWith
({
name
:
'
testing
'
,
type
,
});
...
...
@@ -53,22 +55,4 @@ describe('new file modal component', () => {
});
});
});
it
(
'
focuses field on mount
'
,
()
=>
{
document
.
body
.
innerHTML
+=
'
<div class="js-test"></div>
'
;
vm
=
createComponent
(
Component
,
{
type
:
'
tree
'
,
branchId
:
'
master
'
,
path
:
''
,
},
'
.js-test
'
,
);
expect
(
document
.
activeElement
).
toBe
(
vm
.
$refs
.
fieldName
);
vm
.
$el
.
remove
();
});
});
spec/javascripts/ide/components/new_dropdown/upload_spec.js
View file @
be74e393
...
...
@@ -9,7 +9,6 @@ describe('new dropdown upload', () => {
const
Component
=
Vue
.
extend
(
upload
);
vm
=
createComponent
(
Component
,
{
branchId
:
'
master
'
,
path
:
''
,
});
...
...
@@ -65,7 +64,6 @@ describe('new dropdown upload', () => {
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
create
'
,
{
name
:
file
.
name
,
branchId
:
'
master
'
,
type
:
'
blob
'
,
content
:
target
.
result
,
base64
:
false
,
...
...
@@ -77,7 +75,6 @@ describe('new dropdown upload', () => {
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
create
'
,
{
name
:
file
.
name
,
branchId
:
'
master
'
,
type
:
'
blob
'
,
content
:
binaryTarget
.
result
.
split
(
'
base64,
'
)[
1
],
base64
:
true
,
...
...
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