Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
21543f57d6
commit
c7cb372557
43 changed files with 198 additions and 630 deletions
|
@ -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'
|
||||
|
|
|
@ -1 +1 @@
|
|||
59efecafe0838b6c940f67b00726c8c748d7dad5
|
||||
5d9c71d8d7188bd58f272dc62a7939ece747909d
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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.')));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' }
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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" } }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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 } ×
|
||||
|
||||
.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')
|
|
@ -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
|
|
@ -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")
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Vue notifications dropdown component
|
||||
merge_request: 54422
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix empty field in custom notification events modal
|
||||
merge_request: 55313
|
||||
author: Kev @KevSlashNull
|
||||
type: fixed
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
80
doc/development/usage_ping/product_intelligence_review.md
Normal file
80
doc/development/usage_ping/product_intelligence_review.md
Normal file
|
@ -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.
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
||||
page.within('.custom-notifications-form') do
|
||||
wait_for_requests
|
||||
|
||||
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
|
||||
|
|
|
@ -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}`);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue