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
6507a9e5
Commit
6507a9e5
authored
Aug 30, 2021
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
33c72d41
8f4b7c72
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
300 additions
and
52 deletions
+300
-52
app/assets/javascripts/graphql_shared/constants.js
app/assets/javascripts/graphql_shared/constants.js
+2
-0
app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue
.../pages/projects/new/components/new_project_url_select.vue
+98
-0
app/assets/javascripts/pages/projects/new/index.js
app/assets/javascripts/pages/projects/new/index.js
+32
-8
app/assets/javascripts/pages/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql
...h_namespaces_where_user_can_create_projects.query.graphql
+14
-0
app/assets/javascripts/vue_shared/components/commit.vue
app/assets/javascripts/vue_shared/components/commit.vue
+11
-17
app/views/groups/settings/_permissions.html.haml
app/views/groups/settings/_permissions.html.haml
+2
-4
app/views/projects/_new_project_fields.html.haml
app/views/projects/_new_project_fields.html.haml
+12
-10
ee/app/views/groups/settings/_membership.html.haml
ee/app/views/groups/settings/_membership.html.haml
+0
-6
ee/config/feature_flags/development/saas_user_caps.yml
ee/config/feature_flags/development/saas_user_caps.yml
+0
-0
ee/spec/features/projects/new_project_spec.rb
ee/spec/features/projects/new_project_spec.rb
+1
-0
locale/gitlab.pot
locale/gitlab.pot
+0
-3
spec/features/projects/import_export/import_file_spec.rb
spec/features/projects/import_export/import_file_spec.rb
+1
-0
spec/features/projects/new_project_spec.rb
spec/features/projects/new_project_spec.rb
+4
-0
spec/features/projects/user_creates_project_spec.rb
spec/features/projects/user_creates_project_spec.rb
+1
-0
spec/frontend/pages/projects/new/components/new_project_url_select_spec.js
...es/projects/new/components/new_project_url_select_spec.js
+122
-0
spec/frontend/vue_shared/components/commit_spec.js
spec/frontend/vue_shared/components/commit_spec.js
+0
-4
No files found.
app/assets/javascripts/graphql_shared/constants.js
View file @
6507a9e5
export
const
MINIMUM_SEARCH_LENGTH
=
3
;
export
const
TYPE_CI_RUNNER
=
'
Ci::Runner
'
;
export
const
TYPE_EPIC
=
'
Epic
'
;
export
const
TYPE_GROUP
=
'
Group
'
;
...
...
app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue
0 → 100644
View file @
6507a9e5
<
script
>
import
{
GlButton
,
GlButtonGroup
,
GlDropdown
,
GlDropdownItem
,
GlDropdownSectionHeader
,
GlLoadingIcon
,
GlSearchBoxByType
,
}
from
'
@gitlab/ui
'
;
import
{
MINIMUM_SEARCH_LENGTH
}
from
'
~/graphql_shared/constants
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
Tracking
from
'
~/tracking
'
;
import
{
DEBOUNCE_DELAY
}
from
'
~/vue_shared/components/filtered_search_bar/constants
'
;
import
searchNamespacesWhereUserCanCreateProjectsQuery
from
'
../queries/search_namespaces_where_user_can_create_projects.query.graphql
'
;
export
default
{
components
:
{
GlButton
,
GlButtonGroup
,
GlDropdown
,
GlDropdownItem
,
GlDropdownSectionHeader
,
GlLoadingIcon
,
GlSearchBoxByType
,
},
mixins
:
[
Tracking
.
mixin
()],
apollo
:
{
currentUser
:
{
query
:
searchNamespacesWhereUserCanCreateProjectsQuery
,
variables
()
{
return
{
search
:
this
.
search
,
};
},
skip
()
{
return
this
.
search
.
length
>
0
&&
this
.
search
.
length
<
MINIMUM_SEARCH_LENGTH
;
},
debounce
:
DEBOUNCE_DELAY
,
},
},
inject
:
[
'
namespaceFullPath
'
,
'
namespaceId
'
,
'
rootUrl
'
,
'
trackLabel
'
],
data
()
{
return
{
currentUser
:
{},
search
:
''
,
selectedNamespace
:
{
id
:
this
.
namespaceId
,
fullPath
:
this
.
namespaceFullPath
,
},
};
},
computed
:
{
userGroups
()
{
return
this
.
currentUser
.
groups
?.
nodes
||
[];
},
userNamespace
()
{
return
this
.
currentUser
.
namespace
||
{};
},
},
methods
:
{
handleClick
({
id
,
fullPath
})
{
this
.
selectedNamespace
=
{
id
:
getIdFromGraphQLId
(
id
),
fullPath
,
};
},
},
};
</
script
>
<
template
>
<gl-button-group
class=
"gl-w-full"
>
<gl-button
label
>
{{
rootUrl
}}
</gl-button>
<gl-dropdown
class=
"gl-w-full"
:text=
"selectedNamespace.fullPath"
toggle-class=
"gl-rounded-top-right-base! gl-rounded-bottom-right-base!"
data-qa-selector=
"select_namespace_dropdown"
@
show=
"track('activate_form_input',
{ label: trackLabel, property: 'project_path' })"
>
<gl-search-box-by-type
v-model.trim=
"search"
/>
<gl-loading-icon
v-if=
"$apollo.queries.currentUser.loading"
/>
<template
v-else
>
<gl-dropdown-section-header>
{{
__
(
'
Groups
'
)
}}
</gl-dropdown-section-header>
<gl-dropdown-item
v-for=
"group of userGroups"
:key=
"group.id"
@
click=
"handleClick(group)"
>
{{
group
.
fullPath
}}
</gl-dropdown-item>
<gl-dropdown-section-header>
{{
__
(
'
Users
'
)
}}
</gl-dropdown-section-header>
<gl-dropdown-item
@
click=
"handleClick(userNamespace)"
>
{{
userNamespace
.
fullPath
}}
</gl-dropdown-item>
</
template
>
</gl-dropdown>
<input
type=
"hidden"
name=
"project[namespace_id]"
:value=
"selectedNamespace.id"
/>
</gl-button-group>
</template>
app/assets/javascripts/pages/projects/new/index.js
View file @
6507a9e5
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
initProjectVisibilitySelector
from
'
../../../project_visibility
'
;
import
initProjectNew
from
'
../../../projects/project_new
'
;
import
NewProjectCreationApp
from
'
./components/app.vue
'
;
import
NewProjectUrlSelect
from
'
./components/new_project_url_select.vue
'
;
initProjectVisibilitySelector
();
initProjectNew
.
bindEvents
(
);
function
initNewProjectCreation
()
{
const
el
=
document
.
querySelector
(
'
.js-new-project-creation
'
);
function
initNewProjectCreation
(
el
)
{
const
{
pushToCreateProjectCommand
,
workingWithProjectsHelpPath
,
...
...
@@ -29,9 +31,6 @@ function initNewProjectCreation(el) {
return
new
Vue
({
el
,
components
:
{
NewProjectCreationApp
,
},
provide
,
render
(
h
)
{
return
h
(
NewProjectCreationApp
,
{
props
});
...
...
@@ -39,6 +38,31 @@ function initNewProjectCreation(el) {
});
}
const
el
=
document
.
querySelector
(
'
.js-new-project-creation
'
);
function
initNewProjectUrlSelect
()
{
const
el
=
document
.
querySelector
(
'
.js-vue-new-project-url-select
'
);
if
(
!
el
)
{
return
undefined
;
}
initNewProjectCreation
(
el
);
Vue
.
use
(
VueApollo
);
return
new
Vue
({
el
,
apolloProvider
:
new
VueApollo
({
defaultClient
:
createDefaultClient
({},
{
assumeImmutableResults
:
true
}),
}),
provide
:
{
namespaceFullPath
:
el
.
dataset
.
namespaceFullPath
,
namespaceId
:
el
.
dataset
.
namespaceId
,
rootUrl
:
el
.
dataset
.
rootUrl
,
trackLabel
:
el
.
dataset
.
trackLabel
,
},
render
:
(
createElement
)
=>
createElement
(
NewProjectUrlSelect
),
});
}
initProjectVisibilitySelector
();
initProjectNew
.
bindEvents
();
initNewProjectCreation
();
initNewProjectUrlSelect
();
app/assets/javascripts/pages/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql
0 → 100644
View file @
6507a9e5
query
searchNamespacesWhereUserCanCreateProjects
(
$search
:
String
)
{
currentUser
{
groups
(
permissionScope
:
CREATE_PROJECTS
,
search
:
$search
)
{
nodes
{
id
fullPath
}
}
namespace
{
id
fullPath
}
}
}
app/assets/javascripts/vue_shared/components/commit.vue
View file @
6507a9e5
...
...
@@ -136,6 +136,9 @@ export default {
refUrl
()
{
return
this
.
commitRef
.
ref_url
||
this
.
commitRef
.
path
;
},
tooltipTitle
()
{
return
this
.
mergeRequestRef
?
this
.
mergeRequestRef
.
title
:
this
.
commitRef
.
name
;
},
},
};
</
script
>
...
...
@@ -148,23 +151,14 @@ export default {
<gl-icon
v-else
name=
"branch"
/>
</div>
<gl-link
v-if=
"mergeRequestRef"
v-gl-tooltip
:href=
"mergeRequestRef.path"
:title=
"mergeRequestRef.title"
class=
"ref-name"
>
{{
mergeRequestRef
.
iid
}}
</gl-link
>
<gl-link
v-else
v-gl-tooltip
:href=
"refUrl"
:title=
"commitRef.name"
class=
"ref-name"
data-testid=
"ref-name"
>
{{
commitRef
.
name
}}
</gl-link
>
<tooltip-on-truncate
:title=
"tooltipTitle"
truncate-target=
"child"
placement=
"top"
>
<gl-link
v-if=
"mergeRequestRef"
:href=
"mergeRequestRef.path"
class=
"ref-name"
>
{{
mergeRequestRef
.
iid
}}
</gl-link>
<gl-link
v-else
:href=
"refUrl"
class=
"ref-name"
data-testid=
"ref-name"
>
{{
commitRef
.
name
}}
</gl-link>
</tooltip-on-truncate>
</
template
>
<gl-icon
name=
"commit"
class=
"commit-icon js-commit-icon"
/>
...
...
app/views/groups/settings/_permissions.html.haml
View file @
6507a9e5
...
...
@@ -4,9 +4,8 @@
%fieldset
%h5
=
_
(
'Permissions'
)
-
unless
::
Feature
.
enabled?
(
:saas_user_caps
)
.form-group
=
render
'shared/allow_request_access'
,
form:
f
.form-group
=
render
'shared/allow_request_access'
,
form:
f
-
if
@group
.
root?
.form-group.gl-mb-3
...
...
@@ -44,6 +43,5 @@
=
render_if_exists
'groups/settings/prevent_forking'
,
f:
f
,
group:
@group
=
render
'groups/settings/two_factor_auth'
,
f:
f
,
group:
@group
=
render_if_exists
'groups/personal_access_token_expiration_policy'
,
f:
f
,
group:
@group
=
render_if_exists
'groups/settings/membership'
,
f:
f
=
render_if_exists
'groups/member_lock_setting'
,
f:
f
,
group:
@group
=
f
.
submit
_
(
'Save changes'
),
class:
'btn gl-button btn-confirm gl-mt-3 js-dirty-submit'
,
data:
{
qa_selector:
'save_permissions_changes_button'
}
app/views/projects/_new_project_fields.html.haml
View file @
6507a9e5
...
...
@@ -14,17 +14,19 @@
%span
=
s_
(
"Project URL"
)
.input-group.gl-flex-nowrap
-
if
current_user
.
can_select_namespace?
.input-group-prepend.flex-shrink-0.has-tooltip
{
title:
root_url
}
.input-group-text
=
root_url
-
namespace_id
=
namespace_id_from
(
params
)
=
f
.
select
(
:namespace_id
,
namespaces_options_with_developer_maintainer_access
(
selected:
namespace_id
,
display_path:
true
,
extra_group:
namespace_id
),
{},
{
class:
'select2 js-select-namespace qa-project-namespace-select block-truncated'
,
data:
{
track_label:
"
#{
track_label
}
"
,
track_event:
"activate_form_input"
,
track_property:
"project_path"
,
track_value:
""
,
qa_selector:
"select_namespace_dropdown"
}})
-
if
Feature
.
enabled?
(
:paginatable_namespace_drop_down_for_project_creation
,
current_user
,
default_enabled: :yaml
)
.js-vue-new-project-url-select
{
data:
{
namespace_full_path:
GroupFinder
.
new
(
current_user
).
execute
(
id:
namespace_id
)
&
.
full_path
,
namespace_id:
namespace_id
,
root_url:
root_url
,
track_label:
track_label
}
}
-
else
.input-group-prepend.flex-shrink-0.has-tooltip
{
title:
root_url
}
.input-group-text
=
root_url
=
f
.
select
(
:namespace_id
,
namespaces_options_with_developer_maintainer_access
(
selected:
namespace_id
,
display_path:
true
,
extra_group:
namespace_id
),
{},
{
class:
'select2 js-select-namespace qa-project-namespace-select block-truncated'
,
data:
{
track_label:
"
#{
track_label
}
"
,
track_event:
"activate_form_input"
,
track_property:
"project_path"
,
track_value:
""
,
qa_selector:
"select_namespace_dropdown"
}})
-
else
.input-group-prepend.static-namespace.flex-shrink-0.has-tooltip
{
title:
user_url
(
current_user
.
username
)
+
'/'
}
.input-group-text.border-0
...
...
ee/app/views/groups/settings/_membership.html.haml
deleted
100644 → 0
View file @
33c72d41
-
return
unless
::
Feature
.
enabled?
(
:saas_user_caps
)
%h5
=
_
(
'Membership'
)
.form-group
{
data:
{
testid:
'membership-form-group'
}
}
=
render
'shared/allow_request_access'
,
form:
f
config/feature_flags/development/saas_user_caps.yml
→
ee/
config/feature_flags/development/saas_user_caps.yml
View file @
6507a9e5
File moved
ee/spec/features/projects/new_project_spec.rb
View file @
6507a9e5
...
...
@@ -6,6 +6,7 @@ RSpec.describe 'New project', :js do
let
(
:user
)
{
create
(
:admin
)
}
before
do
stub_feature_flags
(
paginatable_namespace_drop_down_for_project_creation:
false
)
sign_in
(
user
)
end
...
...
locale/gitlab.pot
View file @
6507a9e5
...
...
@@ -20820,9 +20820,6 @@ msgstr ""
msgid "Members of a group may only view projects they have permission to access"
msgstr ""
msgid "Membership"
msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
...
...
spec/features/projects/import_export/import_file_spec.rb
View file @
6507a9e5
...
...
@@ -10,6 +10,7 @@ RSpec.describe 'Import/Export - project import integration test', :js do
let
(
:export_path
)
{
"
#{
Dir
.
tmpdir
}
/import_file_spec"
}
before
do
stub_feature_flags
(
paginatable_namespace_drop_down_for_project_creation:
false
)
stub_uploads_object_storage
(
FileUploader
)
allow_next_instance_of
(
Gitlab
::
ImportExport
)
do
|
instance
|
allow
(
instance
).
to
receive
(
:storage_path
).
and_return
(
export_path
)
...
...
spec/features/projects/new_project_spec.rb
View file @
6507a9e5
...
...
@@ -6,6 +6,10 @@ RSpec.describe 'New project', :js do
include
Select2Helper
include
Spec
::
Support
::
Helpers
::
Features
::
TopNavSpecHelpers
before
do
stub_feature_flags
(
paginatable_namespace_drop_down_for_project_creation:
false
)
end
context
'as a user'
do
let
(
:user
)
{
create
(
:user
)
}
...
...
spec/features/projects/user_creates_project_spec.rb
View file @
6507a9e5
...
...
@@ -6,6 +6,7 @@ RSpec.describe 'User creates a project', :js do
let
(
:user
)
{
create
(
:user
)
}
before
do
stub_feature_flags
(
paginatable_namespace_drop_down_for_project_creation:
false
)
sign_in
(
user
)
create
(
:personal_key
,
user:
user
)
end
...
...
spec/frontend/pages/projects/new/components/new_project_url_select_spec.js
0 → 100644
View file @
6507a9e5
import
{
GlButton
,
GlDropdown
,
GlDropdownItem
,
GlDropdownSectionHeader
}
from
'
@gitlab/ui
'
;
import
{
createLocalVue
,
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
{
mockTracking
,
unmockTracking
}
from
'
helpers/tracking_helper
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
NewProjectUrlSelect
from
'
~/pages/projects/new/components/new_project_url_select.vue
'
;
import
searchQuery
from
'
~/pages/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql
'
;
describe
(
'
NewProjectUrlSelect component
'
,
()
=>
{
let
wrapper
;
const
data
=
{
currentUser
:
{
groups
:
{
nodes
:
[
{
id
:
'
gid://gitlab/Group/26
'
,
fullPath
:
'
flightjs
'
,
},
{
id
:
'
gid://gitlab/Group/28
'
,
fullPath
:
'
h5bp
'
,
},
],
},
namespace
:
{
id
:
'
gid://gitlab/Namespace/1
'
,
fullPath
:
'
root
'
,
},
},
};
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueApollo
);
const
requestHandlers
=
[[
searchQuery
,
jest
.
fn
().
mockResolvedValue
({
data
})]];
const
apolloProvider
=
createMockApollo
(
requestHandlers
);
const
provide
=
{
namespaceFullPath
:
'
h5bp
'
,
namespaceId
:
'
28
'
,
rootUrl
:
'
https://gitlab.com/
'
,
trackLabel
:
'
blank_project
'
,
};
const
mountComponent
=
({
mountFn
=
shallowMount
}
=
{})
=>
mountFn
(
NewProjectUrlSelect
,
{
localVue
,
apolloProvider
,
provide
});
const
findButtonLabel
=
()
=>
wrapper
.
findComponent
(
GlButton
);
const
findDropdown
=
()
=>
wrapper
.
findComponent
(
GlDropdown
);
const
findHiddenInput
=
()
=>
wrapper
.
find
(
'
input
'
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
renders the root url as a label
'
,
()
=>
{
wrapper
=
mountComponent
();
expect
(
findButtonLabel
().
text
()).
toBe
(
provide
.
rootUrl
);
expect
(
findButtonLabel
().
props
(
'
label
'
)).
toBe
(
true
);
});
it
(
'
renders a dropdown with the initial namespace full path as the text
'
,
()
=>
{
wrapper
=
mountComponent
();
expect
(
findDropdown
().
props
(
'
text
'
)).
toBe
(
provide
.
namespaceFullPath
);
});
it
(
'
renders a dropdown with the initial namespace id in the hidden input
'
,
()
=>
{
wrapper
=
mountComponent
();
expect
(
findHiddenInput
().
attributes
(
'
value
'
)).
toBe
(
provide
.
namespaceId
);
});
it
(
'
renders expected dropdown items
'
,
async
()
=>
{
wrapper
=
mountComponent
({
mountFn
:
mount
});
jest
.
runOnlyPendingTimers
();
await
wrapper
.
vm
.
$nextTick
();
const
listItems
=
wrapper
.
findAll
(
'
li
'
);
expect
(
listItems
.
at
(
0
).
findComponent
(
GlDropdownSectionHeader
).
text
()).
toBe
(
'
Groups
'
);
expect
(
listItems
.
at
(
1
).
text
()).
toBe
(
data
.
currentUser
.
groups
.
nodes
[
0
].
fullPath
);
expect
(
listItems
.
at
(
2
).
text
()).
toBe
(
data
.
currentUser
.
groups
.
nodes
[
1
].
fullPath
);
expect
(
listItems
.
at
(
3
).
findComponent
(
GlDropdownSectionHeader
).
text
()).
toBe
(
'
Users
'
);
expect
(
listItems
.
at
(
4
).
text
()).
toBe
(
data
.
currentUser
.
namespace
.
fullPath
);
});
it
(
'
updates hidden input with selected namespace
'
,
async
()
=>
{
wrapper
=
mountComponent
();
jest
.
runOnlyPendingTimers
();
await
wrapper
.
vm
.
$nextTick
();
wrapper
.
findComponent
(
GlDropdownItem
).
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
findHiddenInput
().
attributes
()).
toMatchObject
({
name
:
'
project[namespace_id]
'
,
value
:
getIdFromGraphQLId
(
data
.
currentUser
.
groups
.
nodes
[
0
].
id
).
toString
(),
});
});
it
(
'
tracks clicking on the dropdown
'
,
()
=>
{
wrapper
=
mountComponent
();
const
trackingSpy
=
mockTracking
(
undefined
,
wrapper
.
element
,
jest
.
spyOn
);
findDropdown
().
vm
.
$emit
(
'
show
'
);
expect
(
trackingSpy
).
toHaveBeenCalledWith
(
undefined
,
'
activate_form_input
'
,
{
label
:
provide
.
trackLabel
,
property
:
'
project_path
'
,
});
unmockTracking
();
});
});
spec/frontend/vue_shared/components/commit_spec.js
View file @
6507a9e5
...
...
@@ -162,8 +162,6 @@ describe('Commit component', () => {
expect
(
refEl
.
attributes
(
'
href
'
)).
toBe
(
props
.
commitRef
.
ref_url
);
expect
(
refEl
.
attributes
(
'
title
'
)).
toBe
(
props
.
commitRef
.
name
);
expect
(
findIcon
(
'
branch
'
).
exists
()).
toBe
(
true
);
});
});
...
...
@@ -195,8 +193,6 @@ describe('Commit component', () => {
expect
(
refEl
.
attributes
(
'
href
'
)).
toBe
(
props
.
mergeRequestRef
.
path
);
expect
(
refEl
.
attributes
(
'
title
'
)).
toBe
(
props
.
mergeRequestRef
.
title
);
expect
(
findIcon
(
'
git-merge
'
).
exists
()).
toBe
(
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