diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue | 26 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/common.scss | 2 | ||||
-rw-r--r-- | app/controllers/dashboard/projects_controller.rb | 5 | ||||
-rw-r--r-- | app/models/issue.rb | 1 | ||||
-rw-r--r-- | app/models/service.rb | 9 | ||||
-rw-r--r-- | app/services/boards/issues/list_service.rb | 2 | ||||
-rw-r--r-- | app/services/metrics/dashboard/grafana_metric_embed_service.rb | 20 |
7 files changed, 58 insertions, 7 deletions
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue index b90f441b8ec..5e41a155ef6 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue @@ -122,9 +122,14 @@ export default { this.$store.subscribeAction({ after: this.handleVuexActionDispatch, }); + + document.addEventListener('click', this.handleDocumentClick); + }, + beforeDestroy() { + document.removeEventListener('click', this.handleDocumentClick); }, methods: { - ...mapActions(['setInitialState']), + ...mapActions(['setInitialState', 'toggleDropdownContents']), /** * This method differentiates between * dispatched actions and calls necessary method. @@ -138,6 +143,22 @@ export default { this.handleDropdownClose(state.labels.filter(label => label.touched)); } }, + /** + * This method listens for document-wide click event + * and toggle dropdown if user clicks anywhere outside + * the dropdown while dropdown is visible. + */ + handleDocumentClick({ target }) { + if ( + this.showDropdownButton && + this.showDropdownContents && + !target?.classList.contains('js-sidebar-dropdown-toggle') && + !this.$refs.dropdownButtonCollapsed?.$el.contains(target) && + !this.$refs.dropdownContents?.$el.contains(target) + ) { + this.toggleDropdownContents(); + } + }, handleDropdownClose(labels) { // Only emit label updates if there are any labels to update // on UI. @@ -156,6 +177,7 @@ export default { <div v-if="!dropdownOnly"> <dropdown-value-collapsed v-if="allowLabelCreate" + ref="dropdownButtonCollapsed" :labels="selectedLabels" @onValueClick="handleCollapsedValueClick" /> @@ -167,7 +189,7 @@ export default { <slot></slot> </dropdown-value> <dropdown-button v-show="showDropdownButton" /> - <dropdown-contents v-if="showDropdownButton && showDropdownContents" /> + <dropdown-contents v-if="showDropdownButton && showDropdownContents" ref="dropdownContents" /> </div> </div> </template> diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 408ca249be2..20846502e85 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -413,6 +413,7 @@ img.emoji { .prepend-left-20 { margin-left: 20px; } .prepend-left-32 { margin-left: 32px; } .prepend-left-64 { margin-left: 64px; } +.append-right-2 { margin-right: 2px; } .append-right-4 { margin-right: 4px; } .append-right-5 { margin-right: 5px; } .append-right-8 { margin-right: 8px; } @@ -424,6 +425,7 @@ img.emoji { .append-right-48 { margin-right: 48px; } .prepend-right-32 { margin-right: 32px; } .append-bottom-0 { margin-bottom: 0; } +.append-bottom-2 { margin-bottom: 2px; } .append-bottom-4 { margin-bottom: $gl-padding-4; } .append-bottom-5 { margin-bottom: 5px; } .append-bottom-8 { margin-bottom: $grid-size; } diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 711be67f8f9..039991e07a2 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -33,7 +33,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def starred @projects = load_projects(params.merge(starred: true)) - .includes(:forked_from_project, :tags).page(params[:page]) + .includes(:forked_from_project, :tags) @groups = [] @@ -51,7 +51,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController private def projects - @projects ||= load_projects(params.merge(non_public: true)).page(params[:page]) + @projects ||= load_projects(params.merge(non_public: true)) end def render_projects @@ -73,6 +73,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController .execute .includes(:route, :creator, :group, namespace: [:route, :owner]) .preload(:project_feature) + .page(finder_params[:page]) prepare_projects_for_rendering(projects) end diff --git a/app/models/issue.rb b/app/models/issue.rb index d3f597c0bda..ef65b002816 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -67,6 +67,7 @@ class Issue < ApplicationRecord scope :order_due_date_desc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'DESC')) } scope :order_closest_future_date, -> { reorder(Arel.sql('CASE WHEN issues.due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - issues.due_date) ASC')) } scope :order_relative_position_asc, -> { reorder(::Gitlab::Database.nulls_last_order('relative_position', 'ASC')) } + scope :order_closed_date_desc, -> { reorder(closed_at: :desc) } scope :preload_associated_models, -> { preload(:labels, project: :namespace) } scope :with_api_entity_associations, -> { preload(:timelogs, :assignees, :author, :notes, :labels, project: [:route, { namespace: :route }] ) } diff --git a/app/models/service.rb b/app/models/service.rb index 91597c51fca..8f1772e67f9 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -32,9 +32,12 @@ class Service < ApplicationRecord belongs_to :project, inverse_of: :services has_one :service_hook - validates :project_id, presence: true, unless: -> { template? } + validates :project_id, presence: true, unless: -> { template? || instance? } + validates :project_id, absence: true, if: -> { instance? } validates :type, presence: true validates :template, uniqueness: { scope: :type }, if: -> { template? } + validates :instance, uniqueness: { scope: :type }, if: -> { instance? } + validate :validate_is_instance_or_template scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') } scope :issue_trackers, -> { where(category: 'issue_tracker') } @@ -326,6 +329,10 @@ class Service < ApplicationRecord private + def validate_is_instance_or_template + errors.add(:template, 'The service should be a service template or instance-level integration') if template? && instance? + end + def cache_project_has_external_issue_tracker if project && !project.destroyed? project.cache_has_external_issue_tracker diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb index 699fa17cb65..337710b60e0 100644 --- a/app/services/boards/issues/list_service.rb +++ b/app/services/boards/issues/list_service.rb @@ -10,6 +10,8 @@ module Boards end def execute + return fetch_issues.order_closed_date_desc if list&.closed? + fetch_issues.order_by_position_and_priority(with_cte: can_attempt_search_optimization?) end diff --git a/app/services/metrics/dashboard/grafana_metric_embed_service.rb b/app/services/metrics/dashboard/grafana_metric_embed_service.rb index 3ad3a2c609e..274057b8262 100644 --- a/app/services/metrics/dashboard/grafana_metric_embed_service.rb +++ b/app/services/metrics/dashboard/grafana_metric_embed_service.rb @@ -138,7 +138,9 @@ module Metrics end # Identifies the name of the datasource for a dashboard - # based on the panelId query parameter found in the url + # based on the panelId query parameter found in the url. + # + # If no panel is specified, defaults to the first valid panel. class DatasourceNameParser def initialize(grafana_url, grafana_dashboard) @grafana_url, @grafana_dashboard = grafana_url, grafana_dashboard @@ -146,15 +148,29 @@ module Metrics def parse @grafana_dashboard[:dashboard][:panels] - .find { |panel| panel[:id].to_s == query_params[:panelId] } + .find { |panel| panel_id ? matching_panel?(panel) : valid_panel?(panel) } .try(:[], :datasource) end private + def panel_id + query_params[:panelId] + end + def query_params Gitlab::Metrics::Dashboard::Url.parse_query(@grafana_url) end + + def matching_panel?(panel) + panel[:id].to_s == panel_id + end + + def valid_panel?(panel) + ::Grafana::Validator + .new(@grafana_dashboard, nil, panel, query_params) + .valid? + end end end end |