summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-01 09:11:01 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-01 09:11:01 +0000
commitc7cb37255796023730d0e31324a533e55e25bc46 (patch)
tree5140bef8ec205dd79b71066aa3ed3a3e4cb00be0
parent21543f57d625a70c3884d1915fa14ad340d01edc (diff)
downloadgitlab-ce-c7cb37255796023730d0e31324a533e55e25bc46.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.haml-lint_todo.yml2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/notifications/components/custom_notifications_modal.vue6
-rw-r--r--app/assets/javascripts/notifications/components/notifications_dropdown.vue6
-rw-r--r--app/assets/javascripts/notifications/components/notifications_dropdown_item.vue8
-rw-r--r--app/assets/javascripts/notifications/constants.js1
-rw-r--r--app/assets/javascripts/notifications_dropdown.js35
-rw-r--r--app/assets/javascripts/notifications_form.js48
-rw-r--r--app/assets/javascripts/pages/groups/shared/group_details.js9
-rw-r--r--app/assets/javascripts/pages/profiles/index/index.js7
-rw-r--r--app/assets/javascripts/pages/profiles/notifications/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js9
-rw-r--r--app/assets/stylesheets/framework/lists.scss5
-rw-r--r--app/controllers/groups_controller.rb1
-rw-r--r--app/controllers/notification_settings_controller.rb61
-rw-r--r--app/controllers/projects_controller.rb4
-rw-r--r--app/graphql/types/ci/job_artifact_file_type_enum.rb3
-rw-r--r--app/views/groups/_home_panel.html.haml7
-rw-r--r--app/views/profiles/notifications/_group_settings.html.haml7
-rw-r--r--app/views/profiles/notifications/_project_settings.html.haml7
-rw-r--r--app/views/profiles/notifications/show.html.haml7
-rw-r--r--app/views/projects/_home_panel.html.haml7
-rw-r--r--app/views/projects/tags/_tag.html.haml2
-rw-r--r--app/views/shared/notifications/_button.html.haml37
-rw-r--r--app/views/shared/notifications/_custom_notifications.html.haml34
-rw-r--r--app/views/shared/notifications/_new_button.html.haml35
-rw-r--r--app/views/shared/notifications/_notification_dropdown.html.haml12
-rw-r--r--changelogs/unreleased/321372-feature-flag-rollout-of-vue_notification_dropdown.yml5
-rw-r--r--changelogs/unreleased/323015-fix-empty-field-in-custom-notification-events-modal.yml5
-rw-r--r--config/feature_flags/development/vue_notification_dropdown.yml8
-rw-r--r--config/routes.rb3
-rw-r--r--doc/api/graphql/reference/index.md54
-rw-r--r--doc/development/usage_ping.md4
-rw-r--r--doc/development/usage_ping/metrics_dictionary.md14
-rw-r--r--doc/development/usage_ping/product_intelligence_review.md80
-rw-r--r--locale/gitlab.pot23
-rw-r--r--spec/controllers/notification_settings_controller_spec.rb202
-rw-r--r--spec/features/groups/show_spec.rb5
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb9
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb34
-rw-r--r--spec/frontend/notifications/components/notifications_dropdown_spec.js2
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb4
-rw-r--r--spec/views/projects/_home_panel.html.haml_spec.rb10
43 files changed, 198 insertions, 630 deletions
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index ac05540aad7..555f2645d6b 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -291,8 +291,6 @@ linters:
- 'app/views/shared/milestones/_sidebar.html.haml'
- 'app/views/shared/milestones/_top.html.haml'
- 'app/views/shared/notes/_hints.html.haml'
- - 'app/views/shared/notifications/_button.html.haml'
- - 'app/views/shared/notifications/_new_button.html.haml'
- 'app/views/shared/runners/_runner_description.html.haml'
- 'app/views/shared/runners/show.html.haml'
- 'app/views/shared/snippets/_header.html.haml'
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 7a9a32e8ebb..66068a748a9 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-59efecafe0838b6c940f67b00726c8c748d7dad5
+5d9c71d8d7188bd58f272dc62a7939ece747909d
diff --git a/app/assets/javascripts/notifications/components/custom_notifications_modal.vue b/app/assets/javascripts/notifications/components/custom_notifications_modal.vue
index 0f628897e17..39f5799f31c 100644
--- a/app/assets/javascripts/notifications/components/custom_notifications_modal.vue
+++ b/app/assets/javascripts/notifications/components/custom_notifications_modal.vue
@@ -115,7 +115,11 @@ export default {
<gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-3" />
<template v-else>
<gl-form-group v-for="event in events" :key="event.id">
- <gl-form-checkbox v-model="event.enabled" @change="updateEvent($event, event)">
+ <gl-form-checkbox
+ v-model="event.enabled"
+ :data-testid="`notification-setting-${event.id}`"
+ @change="updateEvent($event, event)"
+ >
<strong>{{ event.name }}</strong
><gl-loading-icon v-if="event.loading" :inline="true" class="gl-ml-2" />
</gl-form-checkbox>
diff --git a/app/assets/javascripts/notifications/components/notifications_dropdown.vue b/app/assets/javascripts/notifications/components/notifications_dropdown.vue
index e4cedfdb810..365831fcdbf 100644
--- a/app/assets/javascripts/notifications/components/notifications_dropdown.vue
+++ b/app/assets/javascripts/notifications/components/notifications_dropdown.vue
@@ -128,7 +128,8 @@ export default {
<gl-button-group
v-if="isCustomNotification"
v-gl-tooltip="{ title: buttonTooltip }"
- data-testid="notificationButton"
+ data-testid="notification-button"
+ :class="{ disabled: disabled }"
:size="buttonSize"
>
<gl-button
@@ -165,12 +166,13 @@ export default {
<gl-dropdown
v-else
v-gl-tooltip="{ title: buttonTooltip }"
- data-testid="notificationButton"
+ data-testid="notification-button"
:text="buttonText"
:icon="buttonIcon"
:loading="isLoading"
:size="buttonSize"
:disabled="disabled"
+ :class="{ disabled: disabled }"
>
<notifications-dropdown-item
v-for="item in notificationLevels"
diff --git a/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue b/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue
index 73bb9c1b36f..2138372d8ad 100644
--- a/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue
+++ b/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue
@@ -33,7 +33,13 @@ export default {
</script>
<template>
- <gl-dropdown-item is-check-item :is-checked="isActive" @click="$emit('item-selected', level)">
+ <gl-dropdown-item
+ is-check-item
+ :is-checked="isActive"
+ :class="{ 'is-active': isActive }"
+ data-testid="notification-item"
+ @click="$emit('item-selected', level)"
+ >
<div class="gl-display-flex gl-flex-direction-column">
<span class="gl-font-weight-bold">{{ title }}</span>
<span class="gl-text-gray-500">{{ description }}</span>
diff --git a/app/assets/javascripts/notifications/constants.js b/app/assets/javascripts/notifications/constants.js
index c12f6a75f96..4f875977d78 100644
--- a/app/assets/javascripts/notifications/constants.js
+++ b/app/assets/javascripts/notifications/constants.js
@@ -53,6 +53,7 @@ export const i18n = {
reassign_merge_request: s__('NotificationEvent|Reassign merge request'),
reopen_issue: s__('NotificationEvent|Reopen issue'),
reopen_merge_request: s__('NotificationEvent|Reopen merge request'),
+ merge_when_pipeline_succeeds: s__('NotificationEvent|Merge when pipeline succeeds'),
success_pipeline: s__('NotificationEvent|Successful pipeline'),
},
};
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
deleted file mode 100644
index d61defed14d..00000000000
--- a/app/assets/javascripts/notifications_dropdown.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import $ from 'jquery';
-import { Rails } from '~/lib/utils/rails_ujs';
-import { __ } from '~/locale';
-import { deprecatedCreateFlash as Flash } from './flash';
-
-export default function notificationsDropdown() {
- $(document).on('click', '.update-notification', function updateNotificationCallback(e) {
- e.preventDefault();
-
- if ($(this).is('.is-active') && $(this).data('notificationLevel') === 'custom') {
- return;
- }
-
- const notificationLevel = $(this).data('notificationLevel');
- const form = $(this).parents('.notification-form').first();
-
- form.find('.js-notification-loading').toggleClass('spinner');
- if (form.hasClass('no-label')) {
- form.find('.js-notification-loading').toggleClass('hidden');
- form.find('.js-notifications-icon').toggleClass('hidden');
- }
- form.find('#notification_setting_level').val(notificationLevel);
- Rails.fire(form[0], 'submit');
- });
-
- $(document).on('ajax:success', '.notification-form', (e) => {
- const data = e.detail[0];
-
- if (data.saved) {
- $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html);
- } else {
- Flash(__('Failed to save new settings'), 'alert');
- }
- });
-}
diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js
deleted file mode 100644
index 8b90da71bef..00000000000
--- a/app/assets/javascripts/notifications_form.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import $ from 'jquery';
-import { deprecatedCreateFlash as flash } from './flash';
-import axios from './lib/utils/axios_utils';
-import { __ } from './locale';
-
-export default class NotificationsForm {
- constructor() {
- this.toggleCheckbox = this.toggleCheckbox.bind(this);
- this.initEventListeners();
- }
-
- initEventListeners() {
- $(document).on('change', '.js-custom-notification-event', this.toggleCheckbox);
- }
-
- toggleCheckbox(e) {
- const $checkbox = $(e.currentTarget);
- const $parent = $checkbox.closest('.form-check');
-
- this.saveEvent($checkbox, $parent);
- }
-
- // eslint-disable-next-line class-methods-use-this
- showCheckboxLoadingSpinner($parent) {
- $parent.find('.is-loading').removeClass('gl-display-none');
- $parent.find('.is-done').addClass('gl-display-none');
- }
-
- saveEvent($checkbox, $parent) {
- const form = $parent.parents('form').first();
-
- this.showCheckboxLoadingSpinner($parent);
-
- axios[form.attr('method')](form.attr('action'), form.serialize())
- .then(({ data }) => {
- $checkbox.enable();
- if (data.saved) {
- $parent.find('.is-loading').addClass('gl-display-none');
- $parent.find('.is-done').removeClass('gl-display-none');
-
- setTimeout(() => {
- $parent.find('.is-done').addClass('gl-display-none');
- }, 2000);
- }
- })
- .catch(() => flash(__('There was an error saving your notification settings.')));
- }
-}
diff --git a/app/assets/javascripts/pages/groups/shared/group_details.js b/app/assets/javascripts/pages/groups/shared/group_details.js
index 8c272e561db..b076ce68250 100644
--- a/app/assets/javascripts/pages/groups/shared/group_details.js
+++ b/app/assets/javascripts/pages/groups/shared/group_details.js
@@ -7,8 +7,6 @@ import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import { getPagePath, getDashPath } from '~/lib/utils/common_utils';
import initNotificationsDropdown from '~/notifications';
-import notificationsDropdown from '~/notifications_dropdown';
-import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import GroupTabs from './group_tabs';
@@ -22,13 +20,8 @@ export default function initGroupDetails(actionName = 'show') {
new GroupTabs({ parentEl: '.groups-listing', action });
new ShortcutsNavigation();
- new NotificationsForm();
- if (gon.features?.vueNotificationDropdown) {
- initNotificationsDropdown();
- } else {
- notificationsDropdown();
- }
+ initNotificationsDropdown();
new ProjectsList();
diff --git a/app/assets/javascripts/pages/profiles/index/index.js b/app/assets/javascripts/pages/profiles/index/index.js
deleted file mode 100644
index 586b3be8661..00000000000
--- a/app/assets/javascripts/pages/profiles/index/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import notificationsDropdown from '../../../notifications_dropdown';
-import NotificationsForm from '../../../notifications_form';
-
-document.addEventListener('DOMContentLoaded', () => {
- new NotificationsForm(); // eslint-disable-line no-new
- notificationsDropdown();
-});
diff --git a/app/assets/javascripts/pages/profiles/notifications/show/index.js b/app/assets/javascripts/pages/profiles/notifications/show/index.js
index 639f5deb72c..51ba6c7a01e 100644
--- a/app/assets/javascripts/pages/profiles/notifications/show/index.js
+++ b/app/assets/javascripts/pages/profiles/notifications/show/index.js
@@ -1,9 +1,5 @@
import initNotificationsDropdown from '~/notifications';
-import notificationsDropdown from '../../../../notifications_dropdown';
-import NotificationsForm from '../../../../notifications_form';
document.addEventListener('DOMContentLoaded', () => {
- new NotificationsForm(); // eslint-disable-line no-new
- notificationsDropdown();
initNotificationsDropdown();
});
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index e5ec9976ac5..7bb7be45f59 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -7,16 +7,13 @@ import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import leaveByUrl from '~/namespaces/leave_by_url';
import initVueNotificationsDropdown from '~/notifications';
-import NotificationsForm from '~/notifications_form';
import initReadMore from '~/read_more';
import UserCallout from '~/user_callout';
-import notificationsDropdown from '../../../notifications_dropdown';
import Star from '../../../star';
initReadMore();
new Star(); // eslint-disable-line no-new
-new NotificationsForm(); // eslint-disable-line no-new
// eslint-disable-next-line no-new
new UserCallout({
setCalloutPerProject: false,
@@ -43,12 +40,6 @@ if (document.querySelector('.project-show-activity')) {
leaveByUrl('project');
-if (gon.features?.vueNotificationDropdown) {
- initVueNotificationsDropdown();
-} else {
- notificationsDropdown();
-}
-
initVueNotificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index b81e9828dec..df2ba718c72 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -192,11 +192,6 @@ ul.content-list {
display: flex;
align-items: center;
white-space: nowrap;
-
- // Override style that allows the flex-row text to wrap.
- &.allow-wrap {
- white-space: normal;
- }
}
.row-main-content {
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 9d7aebe4505..5de207857bb 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -30,7 +30,6 @@ class GroupsController < Groups::ApplicationController
before_action do
push_frontend_feature_flag(:vue_issuables_list, @group)
- push_frontend_feature_flag(:vue_notification_dropdown, @group, default_enabled: :yaml)
end
before_action do
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
deleted file mode 100644
index 7d8c035c852..00000000000
--- a/app/controllers/notification_settings_controller.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-class NotificationSettingsController < ApplicationController
- before_action :authenticate_user!
-
- feature_category :users
-
- def create
- return render_404 unless can_read?(resource)
-
- @notification_setting = current_user.notification_settings_for(resource)
- @saved = @notification_setting.update(notification_setting_params_for(resource))
-
- render_response
- end
-
- def update
- @notification_setting = current_user.notification_settings.find(params[:id])
- @saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source))
-
- render_response
- end
-
- private
-
- def resource
- @resource ||=
- if params[:project_id].present?
- Project.find(params[:project_id])
- elsif params[:namespace_id].present?
- Group.find(params[:namespace_id])
- end
- end
-
- def can_read?(resource)
- ability_name = resource.class.name.downcase
- ability_name = "read_#{ability_name}".to_sym
-
- can?(current_user, ability_name, resource)
- end
-
- def render_response
- btn_class = nil
-
- if params[:hide_label].present?
- btn_class = 'btn-xs' if params[:project_id].present?
- response_template = 'shared/notifications/_new_button'
- else
- response_template = 'shared/notifications/_button'
- end
-
- render json: {
- html: view_to_html_string(response_template, notification_setting: @notification_setting, btn_class: btn_class),
- saved: @saved
- }
- end
-
- def notification_setting_params_for(source)
- params.require(:notification_setting).permit(NotificationSetting.allowed_fields(source))
- end
-end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 1185bcc9ea8..cd5dd7346ee 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -31,10 +31,6 @@ class ProjectsController < Projects::ApplicationController
# Project Export Rate Limit
before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export]
- before_action do
- push_frontend_feature_flag(:vue_notification_dropdown, @project, default_enabled: :yaml)
- end
-
before_action only: [:edit] do
push_frontend_feature_flag(:allow_editing_commit_messages, @project)
end
diff --git a/app/graphql/types/ci/job_artifact_file_type_enum.rb b/app/graphql/types/ci/job_artifact_file_type_enum.rb
index 4b484dec590..5099b0d4850 100644
--- a/app/graphql/types/ci/job_artifact_file_type_enum.rb
+++ b/app/graphql/types/ci/job_artifact_file_type_enum.rb
@@ -6,7 +6,8 @@ module Types
graphql_name 'JobArtifactFileType'
::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.keys.each do |file_type|
- value file_type.to_s.upcase, value: file_type.to_s
+ description = file_type == :codequality ? "CODE QUALITY" : file_type.to_s.titleize.upcase # This is needed as doc lint will not allow codequality as one word
+ value file_type.to_s.upcase, value: file_type.to_s, description: "#{description} job artifact file type."
end
end
end
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 37c4ecc09f3..98ac4515bd8 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -23,11 +23,8 @@
.home-panel-buttons.col-md-12.col-lg-6
- if current_user
.gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } }
- - if Feature.enabled?(:vue_notification_dropdown, @group, default_enabled: :yaml)
- - if @notification_setting
- .js-vue-notification-dropdown{ data: { disabled: emails_disabled, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mr-3 gl-mt-3 gl-vertical-align-top' } }
- - else
- = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn gl-button gl-sm-w-auto gl-w-full', dropdown_container_class: 'gl-mr-0 gl-px-2 gl-sm-w-auto gl-w-full', emails_disabled: emails_disabled
+ - if @notification_setting
+ .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mr-3 gl-mt-3 gl-vertical-align-top' } }
- if can_create_subgroups
.gl-px-2.gl-sm-w-auto.gl-w-full
= link_to _("New subgroup"), new_group_path(parent_id: @group.id), class: "btn btn-success btn-md gl-button btn-success-secondary gl-mt-3 gl-sm-w-auto gl-w-full", data: { qa_selector: 'new_subgroup_button' }
diff --git a/app/views/profiles/notifications/_group_settings.html.haml b/app/views/profiles/notifications/_group_settings.html.haml
index abbfbd995b6..82083af9ff1 100644
--- a/app/views/profiles/notifications/_group_settings.html.haml
+++ b/app/views/profiles/notifications/_group_settings.html.haml
@@ -9,11 +9,8 @@
= link_to group.name, group_path(group)
.table-section.section-30.text-right
- - if Feature.enabled?(:vue_notification_dropdown, default_enabled: :yaml)
- - if setting
- .js-vue-notification-dropdown{ data: { disabled: emails_disabled, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, group_id: group.id, container_class: 'gl-mr-3', show_label: "true" } }
- - else
- = render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled
+ - if setting
+ .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, group_id: group.id, container_class: 'gl-mr-3', show_label: "true" } }
.table-section.section-30
= form_for setting, url: profile_notifications_group_path(group), method: :put, html: { class: 'update-notifications gl-display-flex' } do |f|
diff --git a/app/views/profiles/notifications/_project_settings.html.haml b/app/views/profiles/notifications/_project_settings.html.haml
index 8cd552caa3d..e6953d1b32e 100644
--- a/app/views/profiles/notifications/_project_settings.html.haml
+++ b/app/views/profiles/notifications/_project_settings.html.haml
@@ -8,8 +8,5 @@
= link_to_project(project)
.float-right
- - if Feature.enabled?(:vue_notification_dropdown, default_enabled: :yaml)
- - if setting
- .js-vue-notification-dropdown{ data: { disabled: emails_disabled, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, project_id: project.id, container_class: 'gl-mr-3', show_label: "true" } }
- - else
- = render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled
+ - if setting
+ .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, project_id: project.id, container_class: 'gl-mr-3', show_label: "true" } }
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index cb0ada414ed..853188c563f 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -32,11 +32,8 @@
%br
.clearfix
.form-group.float-left.global-notification-setting
- - if Feature.enabled?(:vue_notification_dropdown, default_enabled: :yaml)
- - if @global_notification_setting
- .js-vue-notification-dropdown{ data: { dropdown_items: notification_dropdown_items(@global_notification_setting).to_json, notification_level: @global_notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), show_label: 'true' } }
- - else
- = render 'shared/notifications/button', notification_setting: @global_notification_setting
+ - if @global_notification_setting
+ .js-vue-notification-dropdown{ data: { dropdown_items: notification_dropdown_items(@global_notification_setting).to_json, notification_level: @global_notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), show_label: 'true' } }
.clearfix
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 0d3049cd9a2..5ebe4a4b741 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -46,11 +46,8 @@
.project-repo-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
- if current_user
.d-inline-flex
- - if Feature.enabled?(:vue_notification_dropdown, @project, default_enabled: :yaml)
- - if @notification_setting
- .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, container_class: 'gl-mr-3 gl-mt-5 gl-vertical-align-top' } }
- - else
- = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn-xs', dropdown_container_class: 'gl-mr-3', emails_disabled: emails_disabled
+ - if @notification_setting
+ .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, container_class: 'gl-mr-3 gl-mt-5 gl-vertical-align-top' } }
.count-buttons.d-inline-flex
= render 'projects/buttons/star'
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 61b357831fd..1072d5bce06 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -2,7 +2,7 @@
- release = @releases.find { |release| release.tag == tag.name }
- commit_status = @tag_pipeline_statuses[tag.name] unless @tag_pipeline_statuses.nil?
-%li.flex-row.allow-wrap.js-tag-list
+%li.flex-row.js-tag-list{ class: "gl-white-space-normal!" }
.row-main-content
= sprite_icon('tag')
= link_to tag.name, project_tag_path(@project, tag.name), class: 'item-title ref-name'
diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml
deleted file mode 100644
index e12531b8a8d..00000000000
--- a/app/views/shared/notifications/_button.html.haml
+++ /dev/null
@@ -1,37 +0,0 @@
-- btn_class = local_assigns.fetch(:btn_class, '')
-- emails_disabled = local_assigns.fetch(:emails_disabled, false)
-
-- if notification_setting
- - if emails_disabled
- - button_title = notification_description(:owner_disabled)
- - aria_label = button_title
- - btn_class << " disabled"
- - else
- - button_title = _("Notification setting")
- - aria_label = _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }
-
- .js-notification-dropdown.notification-dropdown.mr-md-2.home-panel-action-button.dropdown.inline
- = form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f|
- = hidden_setting_source_input(notification_setting)
- = f.hidden_field :level, class: "notification_setting_level"
- .js-notification-toggle-btns
- %div{ class: ("btn-group" if notification_setting.custom?) }
- - if notification_setting.custom?
- %button.dropdown-new.btn.btn-default.btn-icon.gl-button.has-tooltip.notifications-btn.text-left#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
- = sprite_icon("notifications", css_class: "js-notification-loading")
- = notification_title(notification_setting.level)
- %button.btn.dropdown-toggle.gl-display-flex.gl-align-items-center{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
- = sprite_icon('chevron-down')
- .sr-only Toggle dropdown
- - else
- %button.dropdown-new.btn.btn-default.btn-icon.gl-button.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
- .float-left
- = sprite_icon("notifications", css_class: "js-notification-loading")
- = notification_title(notification_setting.level)
- .float-right
- = sprite_icon("chevron-down")
-
- = render "shared/notifications/notification_dropdown", notification_setting: notification_setting
-
- = content_for :scripts_body do
- = render "shared/notifications/custom_notifications", notification_setting: notification_setting
diff --git a/app/views/shared/notifications/_custom_notifications.html.haml b/app/views/shared/notifications/_custom_notifications.html.haml
deleted file mode 100644
index 946e3c67dcf..00000000000
--- a/app/views/shared/notifications/_custom_notifications.html.haml
+++ /dev/null
@@ -1,34 +0,0 @@
-- hide_label = local_assigns.fetch(:hide_label, false)
-
-.modal.fade{ tabindex: "-1", role: "dialog", id: notifications_menu_identifier("modal", notification_setting), "aria-labelledby": "custom-notifications-title" }
- .modal-dialog
- .modal-content
- .modal-header
- %h4#custom-notifications-title.modal-title
- #{ _('Custom notification events') }
- %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
- %span{ "aria-hidden": true } &times;
-
- .modal-body
- .container-fluid
- = form_for notification_setting, html: { class: "custom-notifications-form" } do |f|
- = hidden_setting_source_input(notification_setting)
- = hidden_field_tag("hide_label", true) if hide_label
- .row
- .col-lg-4
- %h4.gl-mt-0= _('Notification events')
- %p
- - notification_link = link_to _('notification emails'), help_page_path('user/profile/notifications'), target: '_blank'
- - paragraph = _('Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.') % { notification_link: notification_link.html_safe }
- #{ paragraph.html_safe }
- .col-lg-8
- - notification_setting.email_events.each_with_index do |event, index|
- - field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]"
- .form-group
- .form-check{ class: ("gl-mt-0" if index == 0) }
- = check_box("notification_setting", event, id: field_id, class: "js-custom-notification-event form-check-input", checked: notification_setting.public_send(event))
- %label.form-check-label{ for: field_id }
- %strong
- = notification_event_name(event)
- %span.spinner.is-loading.gl-vertical-align-middle.gl-display-none
- = sprite_icon('check', css_class: 'is-done gl-display-none gl-vertical-align-middle gl-text-green-600')
diff --git a/app/views/shared/notifications/_new_button.html.haml b/app/views/shared/notifications/_new_button.html.haml
deleted file mode 100644
index 4b008601783..00000000000
--- a/app/views/shared/notifications/_new_button.html.haml
+++ /dev/null
@@ -1,35 +0,0 @@
-- btn_class = local_assigns.fetch(:btn_class, '')
-- dropdown_container_class = local_assigns.fetch(:dropdown_container_class, '')
-- emails_disabled = local_assigns.fetch(:emails_disabled, false)
-
-- if notification_setting
- - if emails_disabled
- - button_title = notification_description(:owner_disabled)
- - btn_class << " disabled"
- - else
- - button_title = _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }
-
- .js-notification-dropdown.notification-dropdown.home-panel-action-button.gl-mt-3.dropdown.inline{ class: dropdown_container_class }
- = form_for notification_setting, remote: true, html: { class: "notification-form no-label" } do |f|
- = hidden_setting_source_input(notification_setting)
- = hidden_field_tag "hide_label", true
- = f.hidden_field :level, class: "notification_setting_level"
- .js-notification-toggle-btns
- %div{ class: ("btn-group" if notification_setting.custom?) }
- - if notification_setting.custom?
- %button.dropdown-new.btn.gl-button.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => button_title, data: { container: "body", placement: 'top', toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
- = notification_setting_icon(notification_setting)
- %span.js-notification-loading.fa.hidden
- %button.btn.gl-button.btn-default.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" }, class: "#{btn_class}" }
- = sprite_icon("chevron-down", css_class: "icon mr-0")
- .sr-only Toggle dropdown
- - else
- %button.dropdown-new.btn.gl-button.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => button_title, data: { container: "body", placement: 'top', toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
- = notification_setting_icon(notification_setting)
- %span.js-notification-loading.fa.hidden
- = sprite_icon("chevron-down", css_class: "icon")
-
- = render "shared/notifications/notification_dropdown", notification_setting: notification_setting
-
- = content_for :scripts_body do
- = render "shared/notifications/custom_notifications", notification_setting: notification_setting, hide_label: true
diff --git a/app/views/shared/notifications/_notification_dropdown.html.haml b/app/views/shared/notifications/_notification_dropdown.html.haml
deleted file mode 100644
index a6ef2d51171..00000000000
--- a/app/views/shared/notifications/_notification_dropdown.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-%ul.dropdown-menu.dropdown-menu-no-wrap.dropdown-menu-selectable.dropdown-menu-large{ role: "menu", class: [notifications_menu_identifier("dropdown", notification_setting)] }
- - NotificationSetting.levels.each_key do |level|
- - next if level == "custom"
- - next if level == "global" && notification_setting.source.nil?
-
- = notification_list_item(level, notification_setting)
-
- %li.divider
- %li
- %a.update-notification{ href: "#", role: "button", class: ("is-active" if notification_setting.custom?), data: { toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), notification_level: "custom", notification_title: "Custom" } }
- %strong.dropdown-menu-inner-title= s_('NotificationSetting|Custom')
- %span.dropdown-menu-inner-content= notification_description("custom")
diff --git a/changelogs/unreleased/321372-feature-flag-rollout-of-vue_notification_dropdown.yml b/changelogs/unreleased/321372-feature-flag-rollout-of-vue_notification_dropdown.yml
new file mode 100644
index 00000000000..02cd7ab47b6
--- /dev/null
+++ b/changelogs/unreleased/321372-feature-flag-rollout-of-vue_notification_dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Add Vue notifications dropdown component
+merge_request: 54422
+author:
+type: other
diff --git a/changelogs/unreleased/323015-fix-empty-field-in-custom-notification-events-modal.yml b/changelogs/unreleased/323015-fix-empty-field-in-custom-notification-events-modal.yml
new file mode 100644
index 00000000000..70b4e90f911
--- /dev/null
+++ b/changelogs/unreleased/323015-fix-empty-field-in-custom-notification-events-modal.yml
@@ -0,0 +1,5 @@
+---
+title: Fix empty field in custom notification events modal
+merge_request: 55313
+author: Kev @KevSlashNull
+type: fixed
diff --git a/config/feature_flags/development/vue_notification_dropdown.yml b/config/feature_flags/development/vue_notification_dropdown.yml
deleted file mode 100644
index 4c4f67fe928..00000000000
--- a/config/feature_flags/development/vue_notification_dropdown.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: vue_notification_dropdown
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52068
-rollout_issue_url:
-milestone: '13.8'
-type: development
-group: group::optimize
-default_enabled: false
diff --git a/config/routes.rb b/config/routes.rb
index d9cf2e9ebdd..4197aa8dbb7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -166,9 +166,6 @@ Rails.application.routes.draw do
end
end
- # Notification settings
- resources :notification_settings, only: [:create, :update]
-
resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do
member do
post :accept
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 9315993368f..aecc7608f38 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -5417,33 +5417,33 @@ Iteration ID wildcard values.
| Value | Description |
| ----- | ----------- |
-| `ACCESSIBILITY` | |
-| `API_FUZZING` | |
-| `ARCHIVE` | |
-| `BROWSER_PERFORMANCE` | |
-| `CLUSTER_APPLICATIONS` | |
-| `COBERTURA` | |
-| `CODEQUALITY` | |
-| `CONTAINER_SCANNING` | |
-| `COVERAGE_FUZZING` | |
-| `DAST` | |
-| `DEPENDENCY_SCANNING` | |
-| `DOTENV` | |
-| `JUNIT` | |
-| `LICENSE_MANAGEMENT` | |
-| `LICENSE_SCANNING` | |
-| `LOAD_PERFORMANCE` | |
-| `LSIF` | |
-| `METADATA` | |
-| `METRICS` | |
-| `METRICS_REFEREE` | |
-| `NETWORK_REFEREE` | |
-| `PERFORMANCE` | |
-| `REQUIREMENTS` | |
-| `SAST` | |
-| `SECRET_DETECTION` | |
-| `TERRAFORM` | |
-| `TRACE` | |
+| `ACCESSIBILITY` | ACCESSIBILITY job artifact file type. |
+| `API_FUZZING` | API FUZZING job artifact file type. |
+| `ARCHIVE` | ARCHIVE job artifact file type. |
+| `BROWSER_PERFORMANCE` | BROWSER PERFORMANCE job artifact file type. |
+| `CLUSTER_APPLICATIONS` | CLUSTER APPLICATIONS job artifact file type. |
+| `COBERTURA` | COBERTURA job artifact file type. |
+| `CODEQUALITY` | CODE QUALITY job artifact file type. |
+| `CONTAINER_SCANNING` | CONTAINER SCANNING job artifact file type. |
+| `COVERAGE_FUZZING` | COVERAGE FUZZING job artifact file type. |
+| `DAST` | DAST job artifact file type. |
+| `DEPENDENCY_SCANNING` | DEPENDENCY SCANNING job artifact file type. |
+| `DOTENV` | DOTENV job artifact file type. |
+| `JUNIT` | JUNIT job artifact file type. |
+| `LICENSE_MANAGEMENT` | LICENSE MANAGEMENT job artifact file type. |
+| `LICENSE_SCANNING` | LICENSE SCANNING job artifact file type. |
+| `LOAD_PERFORMANCE` | LOAD PERFORMANCE job artifact file type. |
+| `LSIF` | LSIF job artifact file type. |
+| `METADATA` | METADATA job artifact file type. |
+| `METRICS` | METRICS job artifact file type. |
+| `METRICS_REFEREE` | METRICS REFEREE job artifact file type. |
+| `NETWORK_REFEREE` | NETWORK REFEREE job artifact file type. |
+| `PERFORMANCE` | PERFORMANCE job artifact file type. |
+| `REQUIREMENTS` | REQUIREMENTS job artifact file type. |
+| `SAST` | SAST job artifact file type. |
+| `SECRET_DETECTION` | SECRET DETECTION job artifact file type. |
+| `TERRAFORM` | TERRAFORM job artifact file type. |
+| `TRACE` | TRACE job artifact file type. |
### ListLimitMetric
diff --git a/doc/development/usage_ping.md b/doc/development/usage_ping.md
index fc807b4a2f8..7fa253e75f5 100644
--- a/doc/development/usage_ping.md
+++ b/doc/development/usage_ping.md
@@ -752,7 +752,7 @@ alt_usage_data(999)
### Adding counters to build new metrics
-When adding the results of two counters, use the `add` usage data method that
+When adding the results of two counters, use the `add` usage data method that
handles fallback values and exceptions. It also generates a valid [SQL export](#exporting-usage-ping-sql-queries-and-definitions).
Example usage:
@@ -869,7 +869,7 @@ Ensure you comply with the [Changelog entries guide](changelog.md).
### 9. Ask for a Product Intelligence Review
-On GitLab.com, we have DangerBot setup to monitor Product Intelligence related files and DangerBot recommends a Product Intelligence review. Mention `@gitlab-org/growth/product_intelligence/engineers` in your MR for a review.
+On GitLab.com, we have DangerBot setup to monitor Product Intelligence related files and DangerBot recommends a [Product Intelligence review](usage_ping/product_intelligence_review.md). Mention `@gitlab-org/growth/product_intelligence/engineers` in your MR for a review.
### 10. Verify your metric
diff --git a/doc/development/usage_ping/metrics_dictionary.md b/doc/development/usage_ping/metrics_dictionary.md
index f27e258a9c2..a1f24ccd99c 100644
--- a/doc/development/usage_ping/metrics_dictionary.md
+++ b/doc/development/usage_ping/metrics_dictionary.md
@@ -35,14 +35,14 @@ Each metric is defined in a separate YAML file consisting of a number of fields:
| `value_type` | yes | `string`; one of `string`, `number`, `boolean`. |
| `status` | yes | `string`; status of the metric, may be set to `data_available`, `planned`, `in_progress`, `implemented`, `not_used`, `deprecated` |
| `time_frame` | yes | `string`; may be set to a value like `7d`, `28d`, `all`, `none`. |
-| `data_source` | yes | `string`: may be set to a value like `database`, `redis`, `redis_hll`, `prometheus`, `ruby`. |
-| `distribution` | yes | The [distribution](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the metric applies. |
-| `tier` | yes | The [tier]( https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/) where the metric applies. |
+| `data_source` | yes | `string`; may be set to a value like `database`, `redis`, `redis_hll`, `prometheus`, `ruby`. |
+| `distribution` | yes | `array`; may be set to one of `ce, ee` or `ee`. The [distribution](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the tracked feature is available. |
+| `tier` | yes | `array`; may be set to one of `free, premium, ultimate`, `premium, ultimate` or `ultimate`. The [tier]( https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/) where the tracked feature is available. |
| `milestone` | no | The milestone when the metric is introduced. |
| `milestone_removed` | no | The milestone when the metric is removed. |
| `introduced_by_url` | no | The URL to the Merge Request that introduced the metric. |
-### Example metric definition
+### Example YAML metric definition
The linked [`uuid`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/license/uuid.yml)
YAML file includes an example metric definition, where the `uuid` metric is the GitLab
@@ -93,3 +93,9 @@ To create a metric definition used in EE, add the `--ee` flag.
bundle exec rails generate gitlab:usage_metric_definition counts.issues --ee --dir=7d
create ee/config/metrics/counts_7d/issues.yml
```
+
+## Metrics added dynamic to Usage Ping payload
+
+The [Redis HLL metrics](../usage_ping.md#known-events-are-added-automatically-in-usage-data-payload) are added automatically to Usage Ping payload.
+
+A YAML metric definition is required for each metric.
diff --git a/doc/development/usage_ping/product_intelligence_review.md b/doc/development/usage_ping/product_intelligence_review.md
new file mode 100644
index 00000000000..852166cc7a3
--- /dev/null
+++ b/doc/development/usage_ping/product_intelligence_review.md
@@ -0,0 +1,80 @@
+---
+stage: Growth
+group: Product Intelligence
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Product Intelligence review guidelines
+
+This page includes introductory material for a
+[Product Intelligence](https://about.gitlab.com/handbook/engineering/development/growth/product-intelligence/)
+review, and is specific to Product Intelligence reviews. For broader advice and
+general best practices for code reviews, refer to our [code review guide](../code_review.md).
+
+## Resources for Product Intelligence reviewers
+
+- [Usage Ping Guide](../usage_ping.md)
+- [Snowplow Guide](../snowplow.md)
+- [Metrics Dictionary](metrics_dictionary.md)
+
+## Review process
+
+We recommend a Product Intelligence review when an application update touches
+Product Intelligence files.
+
+- Changes that touch `usage_data*` files.
+- Changes to the Metrics Dictionary including files in:
+ - [`config/metrics`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/config/metrics).
+ - [`ee/config/metrics`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/config/metrics).
+ - [`dictionary.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/usage_ping/dictionary.md).
+ - [`schema.json`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/schema.json).
+- Changes to `tracking` files.
+- Changes to Product Intelligence tooling. For example,
+ [`Gitlab::UsageMetricDefinitionGenerator`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/generators/gitlab/usage_metric_definition_generator.rb)
+
+### Roles and process
+
+The merge request **author** should:
+
+- Decide whether a Product Intelligence review is needed.
+- If a Product Intelligence review is needed, add the labels
+ `~product intelligence` and `~product intelligence::review pending`.
+- Assign an
+ [engineer](https://gitlab.com/groups/gitlab-org/growth/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) from the Product Intelligence team for a review.
+- Set the correct attributes in YAML metrics:
+ - `product_section`, `product_stage`, `product_group`, `product_category`
+ - Provide a clear description of the metric.
+- Update the
+ [Metrics Dictionary](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/usage_ping/dictionary.md) if it is needed.
+- Add a changelog [according to guidelines](../changelog.md).
+
+The Product Intelligence **reviewer** should:
+
+- Perform a first-pass review on the merge request and suggest improvements to the author.
+- Approve the MR, and relabel the MR with `~"product intelligence::approved"`.
+
+## Review workload distribution
+
+[Danger bot](../dangerbot.md) adds the list of Product Intelligence changed files
+and pings the
+[`@gitlab-org/growth/product-intelligence/engineers`](https://gitlab.com/groups/gitlab-org/growth/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) group for merge requests
+that are not drafts.
+
+Any of the Product Intelligence engineers can be assigned for the Product Intelligence review.
+
+### How to review for Product Intelligence
+
+- Check the [metrics location](../usage_ping.md#1-naming-and-placing-the-metrics) in
+ the Usage Ping JSON payload.
+- Add `~database` label and ask for [database review](../database_review.md) for
+ metrics that are based on Database.
+- For tracking using Redis HLL (HyperLogLog):
+ - Check the Redis slot.
+ - Check if a [feature flag is needed](../usage_ping.md#recommendations).
+- Metrics YAML definitions:
+ - Check the metric `description`.
+ - Check the metrics `key_path`.
+ - Check the `product_section`, `product_stage`, `product_group`, `product_category`.
+ Read the [stages file](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml).
+ - Check the file location. Consider the time frame, and if the file should be under `ee`.
+ - Check the tiers.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 01113ed1020..8bd7c1923a5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -976,6 +976,9 @@ msgstr ""
msgid "'%{name}' Value Stream deleted"
msgstr ""
+msgid "'%{name}' Value Stream saved"
+msgstr ""
+
msgid "'%{name}' stage already exists"
msgstr ""
@@ -8707,7 +8710,7 @@ msgstr ""
msgid "CreateValueStreamForm|'%{name}' Value Stream created"
msgstr ""
-msgid "CreateValueStreamForm|'%{name}' Value Stream edited"
+msgid "CreateValueStreamForm|'%{name}' Value Stream saved"
msgstr ""
msgid "CreateValueStreamForm|Add another stage"
@@ -8986,9 +8989,6 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notificationLinkStart} notification emails%{notificationLinkEnd}."
msgstr ""
-msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
-msgstr ""
-
msgid "Custom project templates"
msgstr ""
@@ -20636,9 +20636,6 @@ msgstr ""
msgid "Notification events"
msgstr ""
-msgid "Notification setting"
-msgstr ""
-
msgid "Notification setting - %{notification_title}"
msgstr ""
@@ -20723,9 +20720,6 @@ msgstr ""
msgid "NotificationLevel|Watch"
msgstr ""
-msgid "NotificationSetting|Custom"
-msgstr ""
-
msgid "Notifications"
msgstr ""
@@ -25971,6 +25965,9 @@ msgstr ""
msgid "Save Changes"
msgstr ""
+msgid "Save Value Stream"
+msgstr ""
+
msgid "Save application"
msgstr ""
@@ -30042,9 +30039,6 @@ msgstr ""
msgid "There was an error saving your changes."
msgstr ""
-msgid "There was an error saving your notification settings."
-msgstr ""
-
msgid "There was an error subscribing to this label."
msgstr ""
@@ -35820,9 +35814,6 @@ msgstr ""
msgid "not found"
msgstr ""
-msgid "notification emails"
-msgstr ""
-
msgid "nounSeries|%{firstItem} and %{lastItem}"
msgstr ""
diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb
deleted file mode 100644
index c4d67df15f7..00000000000
--- a/spec/controllers/notification_settings_controller_spec.rb
+++ /dev/null
@@ -1,202 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe NotificationSettingsController do
- let(:project) { create(:project) }
- let(:group) { create(:group, :internal) }
- let(:user) { create(:user) }
-
- before do
- project.add_developer(user)
- end
-
- describe '#create' do
- context 'when not authorized' do
- it 'redirects to sign in page' do
- post :create,
- params: {
- project_id: project.id,
- notification_setting: { level: :participating }
- }
-
- expect(response).to redirect_to(new_user_session_path)
- end
- end
-
- context 'when authorized' do
- let(:notification_setting) { user.notification_settings_for(source) }
- let(:custom_events) do
- events = {}
-
- NotificationSetting.email_events(source).each do |event|
- events[event.to_s] = true
- end
-
- events
- end
-
- before do
- sign_in(user)
- end
-
- context 'for projects' do
- let(:source) { project }
-
- it 'creates notification setting' do
- post :create,
- params: {
- project_id: project.id,
- notification_setting: { level: :participating }
- }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(notification_setting.level).to eq("participating")
- expect(notification_setting.user_id).to eq(user.id)
- expect(notification_setting.source_id).to eq(project.id)
- expect(notification_setting.source_type).to eq("Project")
- end
-
- context 'with custom settings' do
- it 'creates notification setting' do
- post :create,
- params: {
- project_id: project.id,
- notification_setting: { level: :custom }.merge(custom_events)
- }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(notification_setting.level).to eq("custom")
-
- custom_events.each do |event, value|
- expect(notification_setting.event_enabled?(event)).to eq(value)
- end
- end
- end
- end
-
- context 'for groups' do
- let(:source) { group }
-
- it 'creates notification setting' do
- post :create,
- params: {
- namespace_id: group.id,
- notification_setting: { level: :watch }
- }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(notification_setting.level).to eq("watch")
- expect(notification_setting.user_id).to eq(user.id)
- expect(notification_setting.source_id).to eq(group.id)
- expect(notification_setting.source_type).to eq("Namespace")
- end
-
- context 'with custom settings' do
- it 'creates notification setting' do
- post :create,
- params: {
- namespace_id: group.id,
- notification_setting: { level: :custom }.merge(custom_events)
- }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(notification_setting.level).to eq("custom")
-
- custom_events.each do |event, value|
- expect(notification_setting.event_enabled?(event)).to eq(value)
- end
- end
- end
- end
- end
-
- context 'not authorized' do
- let(:private_project) { create(:project, :private) }
-
- before do
- sign_in(user)
- end
-
- it 'returns 404' do
- post :create,
- params: {
- project_id: private_project.id,
- notification_setting: { level: :participating }
- }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe '#update' do
- let(:notification_setting) { user.global_notification_setting }
-
- context 'when not authorized' do
- it 'redirects to sign in page' do
- put :update,
- params: {
- id: notification_setting,
- notification_setting: { level: :participating }
- }
-
- expect(response).to redirect_to(new_user_session_path)
- end
- end
-
- context 'when authorized' do
- before do
- sign_in(user)
- end
-
- it 'returns success' do
- put :update,
- params: {
- id: notification_setting,
- notification_setting: { level: :participating }
- }
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- context 'and setting custom notification setting' do
- let(:custom_events) do
- events = {}
-
- notification_setting.email_events.each do |event|
- events[event] = "true"
- end
- end
-
- it 'returns success' do
- put :update,
- params: {
- id: notification_setting,
- notification_setting: { level: :participating, events: custom_events }
- }
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
- end
-
- context 'not authorized' do
- let(:other_user) { create(:user) }
-
- before do
- sign_in(other_user)
- end
-
- it 'returns 404' do
- put :update,
- params: {
- id: notification_setting,
- notification_setting: { level: :participating }
- }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-end
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index 5067f11be67..4e213f65108 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -163,7 +163,6 @@ RSpec.describe 'Group show page' do
let!(:project) { create(:project, namespace: group) }
before do
- stub_feature_flags(vue_notification_dropdown: false)
group.add_maintainer(maintainer)
sign_in(maintainer)
end
@@ -171,14 +170,14 @@ RSpec.describe 'Group show page' do
it 'is enabled by default' do
visit path
- expect(page).to have_selector('.notifications-btn:not(.disabled)', visible: true)
+ expect(page).to have_selector('[data-testid="notification-button"]:not(.disabled)')
end
it 'is disabled if emails are disabled' do
group.update_attribute(:emails_disabled, true)
visit path
- expect(page).to have_selector('.notifications-btn.disabled', visible: true)
+ expect(page).to have_selector('[data-testid="notification-button"].disabled')
end
end
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index 289fbff0404..fb133071e7b 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -7,7 +7,6 @@ RSpec.describe 'User visits the notifications tab', :js do
let(:user) { create(:user) }
before do
- stub_feature_flags(vue_notification_dropdown: false)
project.add_maintainer(user)
sign_in(user)
visit(profile_notifications_path)
@@ -16,17 +15,17 @@ RSpec.describe 'User visits the notifications tab', :js do
it 'changes the project notifications setting' do
expect(page).to have_content('Notifications')
- first('#notifications-button').click
- click_link('On mention')
+ first('[data-testid="notification-button"]').click
+ click_button('On mention')
- expect(page).to have_selector('#notifications-button', text: 'On mention')
+ expect(page).to have_selector('[data-testid="notification-button"]', text: 'On mention')
end
context 'when project emails are disabled' do
let(:project) { create(:project, emails_disabled: true) }
it 'notification button is disabled' do
- expect(page).to have_selector('.notifications-btn.disabled', visible: true)
+ expect(page).to have_selector('[data-testid="notification-button"].disabled')
end
end
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index 5f7d9b0963b..9fe8dc920a9 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -6,38 +6,36 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
let(:project) { create(:project, :public, :repository) }
before do
- stub_feature_flags(vue_notification_dropdown: false)
sign_in(project.owner)
end
def click_notifications_button
- first('.notifications-btn').click
+ first('[data-testid="notification-button"]').click
end
it 'changes the notification setting' do
visit project_path(project)
click_notifications_button
- click_link 'On mention'
+ click_button 'On mention'
- page.within('.notification-dropdown') do
- expect(page).not_to have_css('.gl-spinner')
- end
+ wait_for_requests
click_notifications_button
- expect(find('.update-notification.is-active')).to have_content('On mention')
- expect(page).to have_css('.notifications-icon[data-testid="notifications-icon"]')
+
+ page.within first('[data-testid="notification-button"]') do
+ expect(page.find('.gl-new-dropdown-item.is-active')).to have_content('On mention')
+ expect(page).to have_css('[data-testid="notifications-icon"]')
+ end
end
it 'changes the notification setting to disabled' do
visit project_path(project)
click_notifications_button
- click_link 'Disabled'
+ click_button 'Disabled'
- page.within('.notification-dropdown') do
- expect(page).not_to have_css('.gl-spinner')
+ page.within first('[data-testid="notification-button"]') do
+ expect(page).to have_css('[data-testid="notifications-off-icon"]')
end
-
- expect(page).to have_css('.notifications-icon[data-testid="notifications-off-icon"]')
end
context 'custom notification settings' do
@@ -65,11 +63,13 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
it 'shows notification settings checkbox' do
visit project_path(project)
click_notifications_button
- page.find('a[data-notification-level="custom"]').click
+ click_button 'Custom'
+
+ wait_for_requests
- page.within('.custom-notifications-form') do
+ page.within('#custom-notifications-modal') do
email_events.each do |event_name|
- expect(page).to have_selector("input[name='notification_setting[#{event_name}]']")
+ expect(page).to have_selector("[data-testid='notification-setting-#{event_name}']")
end
end
end
@@ -80,7 +80,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
it 'is disabled' do
visit project_path(project)
- expect(page).to have_selector('.notifications-btn.disabled', visible: true)
+ expect(page).to have_selector('[data-testid="notification-button"].disabled', visible: true)
end
end
end
diff --git a/spec/frontend/notifications/components/notifications_dropdown_spec.js b/spec/frontend/notifications/components/notifications_dropdown_spec.js
index 88534a6d690..bc5c977d3e0 100644
--- a/spec/frontend/notifications/components/notifications_dropdown_spec.js
+++ b/spec/frontend/notifications/components/notifications_dropdown_spec.js
@@ -162,7 +162,7 @@ describe('NotificationsDropdown', () => {
initialNotificationLevel: level,
});
- const tooltipElement = findByTestId('notificationButton');
+ const tooltipElement = findByTestId('notification-button');
const tooltip = getBinding(tooltipElement.element, 'gl-tooltip');
expect(tooltip.value.title).toBe(`${tooltipTitlePrefix} - ${title}`);
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index a90cbbf3bd3..14041ad0ac6 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -15,7 +15,7 @@ module CycleAnalyticsHelpers
end
def toggle_dropdown(field)
- page.within("[data-testid='#{field}']") do
+ page.within("[data-testid*='#{field}']") do
find('.dropdown-toggle').click
wait_for_requests
@@ -26,7 +26,7 @@ module CycleAnalyticsHelpers
def select_dropdown_option_by_value(name, value, elem = '.dropdown-item')
toggle_dropdown name
- page.find("[data-testid='#{name}'] .dropdown-menu").find("#{elem}[value='#{value}']").click
+ page.find("[data-testid*='#{name}'] .dropdown-menu").find("#{elem}[value='#{value}']").click
end
def create_commit_referencing_issue(issue, branch_name: generate(:branch))
diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb
index cc0eb9919da..d329c57af00 100644
--- a/spec/views/projects/_home_panel.html.haml_spec.rb
+++ b/spec/views/projects/_home_panel.html.haml_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe 'projects/_home_panel' do
let(:project) { create(:project) }
before do
- stub_feature_flags(vue_notification_dropdown: false)
assign(:project, project)
allow(view).to receive(:current_user).and_return(user)
@@ -25,11 +24,10 @@ RSpec.describe 'projects/_home_panel' do
assign(:notification_setting, notification_settings)
end
- it 'makes it possible to set notification level' do
+ it 'renders Vue app root' do
render
- expect(view).to render_template('shared/notifications/_new_button')
- expect(rendered).to have_selector('.notification-dropdown')
+ expect(rendered).to have_selector('.js-vue-notification-dropdown')
end
end
@@ -40,10 +38,10 @@ RSpec.describe 'projects/_home_panel' do
assign(:notification_setting, nil)
end
- it 'is not possible to set notification level' do
+ it 'does not render Vue app root' do
render
- expect(rendered).not_to have_selector('.notification_dropdown')
+ expect(rendered).not_to have_selector('.js-vue-notification-dropdown')
end
end
end