diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-13 21:08:57 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-13 21:08:57 +0300 |
commit | e9606d7f51144274f9a390c9dd683200daab8eed (patch) | |
tree | b87cf29dc2fb63ea6ed519dabb3f46b06981c737 /app | |
parent | e1189e4c3b8bd5535104f458f5af7505789eac00 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
13 files changed, 339 insertions, 16 deletions
diff --git a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue new file mode 100644 index 00000000000..c1f57be7f97 --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue @@ -0,0 +1,95 @@ +<script> +import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlLoadingIcon } from '@gitlab/ui'; +import { __ } from '~/locale'; +import { PROJECTS_PER_PAGE } from '../constants'; +import getProjectsQuery from '../graphql/queries/get_projects.query.graphql'; + +export default { + PROJECTS_PER_PAGE, + projectQueryPageInfo: { + endCursor: '', + }, + components: { + GlDropdown, + GlDropdownItem, + GlSearchBoxByType, + GlLoadingIcon, + }, + props: { + selectedProject: { + type: Object, + required: false, + default: null, + }, + }, + data() { + return { + initialProjectsLoading: true, + projectSearchQuery: '', + }; + }, + apollo: { + projects: { + query: getProjectsQuery, + variables() { + return { + search: this.projectSearchQuery, + first: this.$options.PROJECTS_PER_PAGE, + after: this.$options.projectQueryPageInfo.endCursor, + searchNamespaces: true, + sort: 'similarity', + }; + }, + update(data) { + return data?.projects?.nodes.filter((project) => !project.repository.empty) ?? []; + }, + result() { + this.initialProjectsLoading = false; + }, + error() { + this.onError({ message: __('Failed to load projects') }); + }, + }, + }, + computed: { + projectsLoading() { + return Boolean(this.$apollo.queries.projects.loading); + }, + projectDropdownText() { + return this.selectedProject?.nameWithNamespace || __('Select a project'); + }, + }, + methods: { + async onProjectSelect(project) { + this.$emit('change', project); + }, + onError({ message } = {}) { + this.$emit('error', { message }); + }, + isProjectSelected(project) { + return project.id === this.selectedProject?.id; + }, + }, +}; +</script> + +<template> + <gl-dropdown :text="projectDropdownText" :loading="initialProjectsLoading"> + <template #header> + <gl-search-box-by-type v-model.trim="projectSearchQuery" :debounce="250" /> + </template> + + <gl-loading-icon v-show="projectsLoading" /> + <template v-if="!projectsLoading"> + <gl-dropdown-item + v-for="project in projects" + :key="project.id" + is-check-item + :is-checked="isProjectSelected(project)" + @click="onProjectSelect(project)" + > + {{ project.nameWithNamespace }} + </gl-dropdown-item> + </template> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue new file mode 100644 index 00000000000..0e2d8821f36 --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue @@ -0,0 +1,134 @@ +<script> +import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlLoadingIcon } from '@gitlab/ui'; +import { __ } from '~/locale'; +import { BRANCHES_PER_PAGE } from '../constants'; +import getProjectQuery from '../graphql/queries/get_project.query.graphql'; + +export default { + BRANCHES_PER_PAGE, + components: { + GlDropdown, + GlDropdownItem, + GlSearchBoxByType, + GlLoadingIcon, + }, + props: { + selectedProject: { + type: Object, + required: false, + default: null, + }, + selectedBranchName: { + type: String, + required: false, + default: null, + }, + }, + data() { + return { + sourceBranchSearchQuery: '', + initialSourceBranchNamesLoading: false, + sourceBranchNamesLoading: false, + sourceBranchNames: [], + }; + }, + computed: { + hasSelectedProject() { + return Boolean(this.selectedProject); + }, + hasSelectedSourceBranch() { + return Boolean(this.selectedBranchName); + }, + branchDropdownText() { + return this.selectedBranchName || __('Select a branch'); + }, + }, + watch: { + selectedProject: { + immediate: true, + async handler(selectedProject) { + if (!selectedProject) return; + + this.initialSourceBranchNamesLoading = true; + await this.fetchSourceBranchNames({ projectPath: selectedProject.fullPath }); + this.initialSourceBranchNamesLoading = false; + }, + }, + }, + methods: { + onSourceBranchSelect(branchName) { + this.$emit('change', branchName); + }, + onSourceBranchSearchQuery(branchSearchQuery) { + this.branchSearchQuery = branchSearchQuery; + this.fetchSourceBranchNames({ + projectPath: this.selectedProject.fullPath, + searchPattern: this.branchSearchQuery, + }); + }, + onError({ message } = {}) { + this.$emit('error', { message }); + }, + async fetchSourceBranchNames({ projectPath, searchPattern } = {}) { + this.sourceBranchNamesLoading = true; + try { + const { data } = await this.$apollo.query({ + query: getProjectQuery, + variables: { + projectPath, + branchNamesLimit: this.$options.BRANCHES_PER_PAGE, + branchNamesOffset: 0, + branchNamesSearchPattern: searchPattern ? `*${searchPattern}*` : '*', + }, + }); + + const { branchNames, rootRef } = data?.project.repository || {}; + this.sourceBranchNames = branchNames || []; + + // Use root ref as the default selection + if (rootRef && !this.hasSelectedSourceBranch) { + this.onSourceBranchSelect(rootRef); + } + } catch (err) { + this.onError({ + message: __('Something went wrong while fetching source branches.'), + }); + } finally { + this.sourceBranchNamesLoading = false; + } + }, + }, +}; +</script> + +<template> + <gl-dropdown + :text="branchDropdownText" + :loading="initialSourceBranchNamesLoading" + :disabled="!hasSelectedProject" + :class="{ 'gl-font-monospace': hasSelectedSourceBranch }" + > + <template #header> + <gl-search-box-by-type + :debounce="250" + :value="sourceBranchSearchQuery" + @input="onSourceBranchSearchQuery" + /> + </template> + + <gl-loading-icon v-show="sourceBranchNamesLoading" /> + <template v-if="!sourceBranchNamesLoading"> + <gl-dropdown-item + v-for="branchName in sourceBranchNames" + v-show="!sourceBranchNamesLoading" + :key="branchName" + :is-checked="branchName === selectedBranchName" + is-check-item + class="gl-font-monospace" + @click="onSourceBranchSelect(branchName)" + > + {{ branchName }} + </gl-dropdown-item> + </template> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/jira_connect/branches/constants.js b/app/assets/javascripts/jira_connect/branches/constants.js new file mode 100644 index 00000000000..987c8d356b4 --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/constants.js @@ -0,0 +1,2 @@ +export const BRANCHES_PER_PAGE = 20; +export const PROJECTS_PER_PAGE = 20; diff --git a/app/assets/javascripts/jira_connect/branches/graphql/queries/get_project.query.graphql b/app/assets/javascripts/jira_connect/branches/graphql/queries/get_project.query.graphql new file mode 100644 index 00000000000..f3428e816d7 --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/graphql/queries/get_project.query.graphql @@ -0,0 +1,17 @@ +query getProject( + $projectPath: ID! + $branchNamesLimit: Int! + $branchNamesOffset: Int! + $branchNamesSearchPattern: String! +) { + project(fullPath: $projectPath) { + repository { + branchNames( + limit: $branchNamesLimit + offset: $branchNamesOffset + searchPattern: $branchNamesSearchPattern + ) + rootRef + } + } +} diff --git a/app/assets/javascripts/jira_connect/branches/graphql/queries/get_projects.query.graphql b/app/assets/javascripts/jira_connect/branches/graphql/queries/get_projects.query.graphql new file mode 100644 index 00000000000..e768154e210 --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/graphql/queries/get_projects.query.graphql @@ -0,0 +1,34 @@ +#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" + +query getProjects( + $search: String! + $after: String = "" + $first: Int! + $searchNamespaces: Boolean = false + $sort: String + $membership: Boolean = true +) { + projects( + search: $search + after: $after + first: $first + membership: $membership + searchNamespaces: $searchNamespaces + sort: $sort + ) { + nodes { + id + name + nameWithNamespace + fullPath + avatarUrl + path + repository { + empty + } + } + pageInfo { + ...PageInfo + } + } +} diff --git a/app/controllers/projects/mattermosts_controller.rb b/app/controllers/projects/mattermosts_controller.rb index aeca350d4ed..ebba20b285a 100644 --- a/app/controllers/projects/mattermosts_controller.rb +++ b/app/controllers/projects/mattermosts_controller.rb @@ -41,6 +41,5 @@ class Projects::MattermostsController < Projects::ApplicationController def integration @integration ||= @project.find_or_initialize_integration('mattermost_slash_commands') - @service = @integration # TODO: remove when https://gitlab.com/gitlab-org/gitlab/-/issues/330300 is complete end end diff --git a/app/controllers/projects/service_hook_logs_controller.rb b/app/controllers/projects/service_hook_logs_controller.rb index 0db72d12012..93ba3eedddb 100644 --- a/app/controllers/projects/service_hook_logs_controller.rb +++ b/app/controllers/projects/service_hook_logs_controller.rb @@ -16,6 +16,5 @@ class Projects::ServiceHookLogsController < Projects::HookLogsController def integration @integration ||= @project.find_or_initialize_integration(params[:service_id]) - @service = @integration # TODO: remove when https://gitlab.com/gitlab-org/gitlab/-/issues/330300 is complete end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index e53fd55396b..ef6d96e8737 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -112,7 +112,7 @@ class Projects::ServicesController < Projects::ApplicationController return if !integration.is_a?(::Integrations::Prometheus) || !Feature.enabled?(:settings_operations_prometheus_service, project) operations_link_start = "<a href=\"#{project_settings_operations_path(project)}\">" - message = s_('PrometheusService|You can now manage your Prometheus settings on the %{operations_link_start}Operations%{operations_link_end} page. Fields on this page has been deprecated.') % { operations_link_start: operations_link_start, operations_link_end: "</a>" } + message = s_('PrometheusService|You can now manage your Prometheus settings on the %{operations_link_start}Operations%{operations_link_end} page. Fields on this page have been deprecated.') % { operations_link_start: operations_link_start, operations_link_end: "</a>" } flash.now[:alert] = message.html_safe end end diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index 0fc306a3f2e..9e3a6a60d75 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -2,25 +2,68 @@ module SidebarsHelper def sidebar_tracking_attributes_by_object(object) + sidebar_attributes_for_object(object).fetch(:tracking_attrs, {}) + end + + def sidebar_qa_selector(object) + sidebar_attributes_for_object(object).fetch(:sidebar_qa_selector, nil) + end + + def scope_qa_menu_item(object) + sidebar_attributes_for_object(object).fetch(:scope_qa_menu_item, nil) + end + + def scope_avatar_classes(object) + %w[avatar-container rect-avatar s32].tap do |klasses| + klass = sidebar_attributes_for_object(object).fetch(:scope_avatar_class, nil) + klasses << klass if klass + end + end + + def project_sidebar_context(project, user, current_ref) + context_data = project_sidebar_context_data(project, user, current_ref) + + Sidebars::Projects::Context.new(**context_data) + end + + private + + def sidebar_attributes_for_object(object) case object when Project - sidebar_project_tracking_attrs + sidebar_project_attributes when Group - sidebar_group_tracking_attrs + sidebar_group_attributes when User - sidebar_user_profile_tracking_attrs + sidebar_user_attributes else {} end end - def project_sidebar_context(project, user, current_ref) - context_data = project_sidebar_context_data(project, user, current_ref) + def sidebar_project_attributes + { + tracking_attrs: sidebar_project_tracking_attrs, + sidebar_qa_selector: 'project_sidebar', + scope_qa_menu_item: 'Project scope', + scope_avatar_class: 'project_avatar' + } + end - Sidebars::Projects::Context.new(**context_data) + def sidebar_group_attributes + { + tracking_attrs: sidebar_group_tracking_attrs, + sidebar_qa_selector: 'group_sidebar', + scope_qa_menu_item: 'Group scope', + scope_avatar_class: 'group_avatar' + } end - private + def sidebar_user_attributes + { + tracking_attrs: sidebar_user_profile_tracking_attrs + } + end def sidebar_project_tracking_attrs tracking_attrs('projects_side_navigation', 'render', 'projects_side_navigation') diff --git a/app/views/projects/mattermosts/_no_teams.html.haml b/app/views/projects/mattermosts/_no_teams.html.haml index f375222e311..adef11f315a 100644 --- a/app/views/projects/mattermosts/_no_teams.html.haml +++ b/app/views/projects/mattermosts/_no_teams.html.haml @@ -15,4 +15,4 @@ and try again. %hr .clearfix - = link_to 'Go back', edit_project_service_path(@project, @service), class: 'gl-button btn btn-lg float-right' + = link_to 'Go back', edit_project_service_path(@project, @integration), class: 'gl-button btn btn-lg float-right' diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml index ea04a55a77c..4109fdfc13b 100644 --- a/app/views/projects/mattermosts/_team_selection.html.haml +++ b/app/views/projects/mattermosts/_team_selection.html.haml @@ -2,7 +2,7 @@ This service will be installed on the Mattermost instance at %strong= link_to Gitlab.config.mattermost.host, Gitlab.config.mattermost.host %hr -= form_for(:mattermost, method: :post, url: project_mattermost_path(@project), html: { class: 'js-requires-input'} ) do |f| += form_for(:mattermost, method: :post, url: project_mattermost_path(@project), html: { class: 'js-requires-input' }) do |f| %h4 Team %p = @teams.one? ? 'The team' : 'Select the team' @@ -42,5 +42,5 @@ %hr .clearfix .float-right - = link_to 'Cancel', edit_project_service_path(@project, @service), class: 'gl-button btn btn-lg' + = link_to 'Cancel', edit_project_service_path(@project, @integration), class: 'gl-button btn btn-lg' = f.submit 'Install', class: 'gl-button btn btn-success btn-lg' diff --git a/app/views/shared/nav/_scope_menu.html.haml b/app/views/shared/nav/_scope_menu.html.haml index 0a544ec5fa6..1a7089fb570 100644 --- a/app/views/shared/nav/_scope_menu.html.haml +++ b/app/views/shared/nav/_scope_menu.html.haml @@ -1,6 +1,6 @@ = nav_link(**scope_menu.active_routes, html_options: scope_menu.nav_link_html_options) do - = link_to scope_menu.link, **scope_menu.container_html_options, data: { qa_selector: 'project_scope_link' } do - %span{ class: ['avatar-container', 'rect-avatar', 'project-avatar', 's32'] } + = link_to scope_menu.link, **scope_menu.container_html_options, data: { qa_selector: 'sidebar_menu_link', qa_menu_item: scope_qa_menu_item(scope_menu.container) } do + %span{ class: scope_avatar_classes(scope_menu.container) } = source_icon(scope_menu.container, alt: scope_menu.title, class: ['avatar', 'avatar-tile', 's32'], width: 32, height: 32) %span.sidebar-context-title = scope_menu.title diff --git a/app/views/shared/nav/_sidebar.html.haml b/app/views/shared/nav/_sidebar.html.haml index 82a64272a95..a52c2f8dd4b 100644 --- a/app/views/shared/nav/_sidebar.html.haml +++ b/app/views/shared/nav/_sidebar.html.haml @@ -3,7 +3,7 @@ - if sidebar.render_raw_scope_menu_partial = render sidebar.render_raw_scope_menu_partial - %ul.sidebar-top-level-items.qa-project-sidebar + %ul.sidebar-top-level-items{ data: { qa_selector: sidebar_qa_selector(sidebar.container) } } - if sidebar.scope_menu = render partial: 'shared/nav/scope_menu', object: sidebar.scope_menu - if sidebar.renderable_menus.any? |