Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
142658ee28
commit
8ca437b794
|
|
@ -0,0 +1,70 @@
|
|||
<script>
|
||||
import ListboxInput from '~/vue_shared/components/listbox_input/listbox_input.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ListboxInput,
|
||||
},
|
||||
inject: {
|
||||
label: {
|
||||
from: 'label',
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
from: 'name',
|
||||
},
|
||||
emails: {
|
||||
from: 'emails',
|
||||
default: () => [],
|
||||
},
|
||||
emptyValueText: {
|
||||
from: 'emptyValueText',
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
from: 'value',
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
from: 'disabled',
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected: this.value,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
options() {
|
||||
return [
|
||||
{
|
||||
value: '',
|
||||
text: this.emptyValueText,
|
||||
},
|
||||
...this.emails.map((email) => ({
|
||||
text: email,
|
||||
value: email,
|
||||
})),
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async onSelect() {
|
||||
await this.$nextTick();
|
||||
this.$el.closest('form').submit();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<listbox-input
|
||||
v-model="selected"
|
||||
:label="label"
|
||||
:name="name"
|
||||
:items="options"
|
||||
:disabled="disabled"
|
||||
@select="onSelect"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -2,10 +2,37 @@ import { GlToast } from '@gitlab/ui';
|
|||
import Vue from 'vue';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import NotificationsDropdown from './components/notifications_dropdown.vue';
|
||||
import NotificationEmailListboxInput from './components/notification_email_listbox_input.vue';
|
||||
|
||||
Vue.use(GlToast);
|
||||
|
||||
const initNotificationEmailListboxInputs = () => {
|
||||
const els = [...document.querySelectorAll('.js-notification-email-listbox-input')];
|
||||
|
||||
els.forEach((el, index) => {
|
||||
const { label, name, emptyValueText, value } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
name: `NotificationEmailListboxInputRoot${index + 1}`,
|
||||
provide: {
|
||||
label,
|
||||
name,
|
||||
emails: JSON.parse(el.dataset.emails),
|
||||
emptyValueText,
|
||||
value,
|
||||
disabled: parseBoolean(el.dataset.disabled),
|
||||
},
|
||||
render(h) {
|
||||
return h(NotificationEmailListboxInput);
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default () => {
|
||||
initNotificationEmailListboxInputs();
|
||||
|
||||
const containers = document.querySelectorAll('.js-vue-notification-dropdown');
|
||||
|
||||
if (!containers.length) return false;
|
||||
|
|
|
|||
|
|
@ -30,10 +30,6 @@ export default class Profile {
|
|||
|
||||
bindEvents() {
|
||||
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
|
||||
$('.js-group-notification-email').on('change', this.submitForm);
|
||||
$('#user_notification_email').on('select2-selecting', (event) => {
|
||||
setTimeout(this.submitForm.bind(event.currentTarget));
|
||||
});
|
||||
$('#user_email_opted_in').on('change', this.submitForm);
|
||||
$('#user_notified_of_own_activity').on('change', this.submitForm);
|
||||
this.form.on('submit', this.onSubmitForm);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -37,6 +42,11 @@ export default {
|
|||
type: GlListbox.props.items.type,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -44,6 +54,9 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
wrapperComponent() {
|
||||
return this.label || this.description ? 'gl-form-group' : 'div';
|
||||
},
|
||||
allOptions() {
|
||||
const allOptions = [];
|
||||
|
||||
|
|
@ -102,16 +115,17 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form-group :label="label">
|
||||
<component :is="wrapperComponent" :label="label" :description="description">
|
||||
<gl-listbox
|
||||
:selected="selected"
|
||||
:toggle-text="toggleText"
|
||||
:items="filteredItems"
|
||||
:searchable="isSearchable"
|
||||
:no-results-text="$options.i18n.noResultsText"
|
||||
:disabled="disabled"
|
||||
@search="search"
|
||||
@select="$emit($options.model.event, $event)"
|
||||
/>
|
||||
<input ref="input" type="hidden" :name="name" :value="selected" />
|
||||
</gl-form-group>
|
||||
</component>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
- form = local_assigns.fetch(:form)
|
||||
.form-group
|
||||
= form.label :notification_email, _('Notification Email'), class: "label-bold"
|
||||
= form.select :notification_email, @user.public_verified_emails, { include_blank: _('Use primary email (%{email})') % { email: @user.email }, selected: @user.notification_email }, class: "select2", disabled: local_assigns.fetch(:email_change_disabled, nil)
|
||||
.js-notification-email-listbox-input{ data: { label: _('Notification Email'), name: 'user[notification_email]', emails: @user.public_verified_emails.to_json, empty_value_text: _('Use primary email (%{email})') % { email: @user.email }, value: @user.notification_email, disabled: local_assigns.fetch(:email_change_disabled, nil) } }
|
||||
.help-block
|
||||
= local_assigns.fetch(:help_text, nil)
|
||||
.form-group
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@
|
|||
|
||||
.table-section.section-30
|
||||
= form_for setting, url: profile_group_notifications_path(group), method: :put, html: { class: 'update-notifications gl-display-flex' } do |f|
|
||||
= f.select :notification_email, @user.public_verified_emails, { include_blank: 'Global notification email' }, class: 'select2 js-group-notification-email'
|
||||
.js-notification-email-listbox-input{ data: { name: 'notification_setting[notification_email]', emails: @user.public_verified_emails.to_json, empty_value_text: _('Global notification email') , value: setting.notification_email } }
|
||||
|
|
|
|||
|
|
@ -18829,6 +18829,9 @@ msgstr ""
|
|||
msgid "Global Shortcuts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Global notification email"
|
||||
msgstr ""
|
||||
|
||||
msgid "Global notification level"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import ListboxInput from '~/vue_shared/components/listbox_input/listbox_input.vue';
|
||||
import NotificationEmailListboxInput from '~/notifications/components/notification_email_listbox_input.vue';
|
||||
|
||||
describe('NotificationEmailListboxInput', () => {
|
||||
let wrapper;
|
||||
|
||||
// Props
|
||||
const label = 'label';
|
||||
const name = 'name';
|
||||
const emails = ['test@gitlab.com'];
|
||||
const emptyValueText = 'emptyValueText';
|
||||
const value = 'value';
|
||||
const disabled = false;
|
||||
|
||||
// Finders
|
||||
const findListboxInput = () => wrapper.findComponent(ListboxInput);
|
||||
|
||||
const createComponent = (attachTo) => {
|
||||
wrapper = shallowMount(NotificationEmailListboxInput, {
|
||||
provide: {
|
||||
label,
|
||||
name,
|
||||
emails,
|
||||
emptyValueText,
|
||||
value,
|
||||
disabled,
|
||||
},
|
||||
attachTo,
|
||||
});
|
||||
};
|
||||
|
||||
describe('props', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it.each`
|
||||
propName | propValue
|
||||
${'label'} | ${label}
|
||||
${'name'} | ${name}
|
||||
${'selected'} | ${value}
|
||||
${'disabled'} | ${disabled}
|
||||
`('passes the $propName prop to ListboxInput', ({ propName, propValue }) => {
|
||||
expect(findListboxInput().props(propName)).toBe(propValue);
|
||||
});
|
||||
|
||||
it('passes the options to ListboxInput', () => {
|
||||
expect(findListboxInput().props('items')).toStrictEqual([
|
||||
{ text: emptyValueText, value: '' },
|
||||
{ text: emails[0], value: emails[0] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('form', () => {
|
||||
let form;
|
||||
|
||||
beforeEach(() => {
|
||||
form = document.createElement('form');
|
||||
const root = document.createElement('div');
|
||||
form.appendChild(root);
|
||||
createComponent(root);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
form = null;
|
||||
});
|
||||
|
||||
it('submits the parent form when the value changes', async () => {
|
||||
jest.spyOn(form, 'submit');
|
||||
expect(form.submit).not.toHaveBeenCalled();
|
||||
|
||||
findListboxInput().vm.$emit('select');
|
||||
await nextTick();
|
||||
|
||||
expect(form.submit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -7,6 +7,7 @@ describe('ListboxInput', () => {
|
|||
|
||||
// Props
|
||||
const label = 'label';
|
||||
const decription = 'decription';
|
||||
const name = 'name';
|
||||
const defaultToggleText = 'defaultToggleText';
|
||||
const items = [
|
||||
|
|
@ -32,6 +33,7 @@ describe('ListboxInput', () => {
|
|||
wrapper = shallowMount(ListboxInput, {
|
||||
propsData: {
|
||||
label,
|
||||
decription,
|
||||
name,
|
||||
defaultToggleText,
|
||||
items,
|
||||
|
|
@ -40,6 +42,23 @@ describe('ListboxInput', () => {
|
|||
});
|
||||
};
|
||||
|
||||
describe('wrapper', () => {
|
||||
it.each`
|
||||
description | labelProp | descriptionProp | rendersGlFormGroup
|
||||
${'does not render'} | ${''} | ${''} | ${false}
|
||||
${'renders'} | ${'labelProp'} | ${''} | ${true}
|
||||
${'renders'} | ${''} | ${'descriptionProp'} | ${true}
|
||||
${'renders'} | ${'labelProp'} | ${'descriptionProp'} | ${true}
|
||||
`(
|
||||
"$description a GlFormGroup when label is '$labelProp' and description is '$descriptionProp'",
|
||||
({ labelProp, descriptionProp, rendersGlFormGroup }) => {
|
||||
createComponent({ label: labelProp, description: descriptionProp });
|
||||
|
||||
expect(findGlFormGroup().exists()).toBe(rendersGlFormGroup);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('options', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
|
|
@ -49,6 +68,10 @@ describe('ListboxInput', () => {
|
|||
expect(findGlFormGroup().attributes('label')).toBe(label);
|
||||
});
|
||||
|
||||
it('passes the decription to the form group', () => {
|
||||
expect(findGlFormGroup().attributes('decription')).toBe(decription);
|
||||
});
|
||||
|
||||
it('sets the input name', () => {
|
||||
expect(findInput().attributes('name')).toBe(name);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ require 'spec_helper'
|
|||
RSpec.describe 'profiles/notifications/show' do
|
||||
let(:groups) { GroupsFinder.new(user).execute.page(1) }
|
||||
let(:user) { create(:user) }
|
||||
let(:option_default) { _('Use primary email (%{email})') % { email: user.email } }
|
||||
let(:option_primary_email) { user.email }
|
||||
let(:expected_primary_email_attr) { "[data-emails='#{[option_primary_email].to_json}']" }
|
||||
let(:expected_default_attr) { "[data-empty-value-text='#{option_default}']" }
|
||||
let(:expected_selector) { expected_primary_email_attr + expected_default_attr + expected_value_attr }
|
||||
|
||||
before do
|
||||
assign(:group_notifications, [])
|
||||
|
|
@ -16,14 +21,26 @@ RSpec.describe 'profiles/notifications/show' do
|
|||
end
|
||||
|
||||
context 'when there is no database value for User#notification_email' do
|
||||
let(:option_default) { _('Use primary email (%{email})') % { email: user.email } }
|
||||
let(:option_primary_email) { user.email }
|
||||
let(:options) { [option_default, option_primary_email] }
|
||||
let(:expected_value_attr) { ":not([data-value])" }
|
||||
|
||||
it 'displays the correct elements' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_select('user_notification_email', options: options, selected: nil)
|
||||
expect(rendered).to have_selector(expected_selector)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a database value for User#notification_email' do
|
||||
let(:expected_value_attr) { "[data-value='#{option_primary_email}']" }
|
||||
|
||||
before do
|
||||
user.notification_email = option_primary_email
|
||||
end
|
||||
|
||||
it 'displays the correct elements' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector(expected_selector)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue