Commit 608675f7 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'simplify-notifications-dropdown' into 'master'

Simplify notifications dropdown

See merge request gitlab-org/gitlab!55522
parents 53295e35 38228cd9
...@@ -24,12 +24,21 @@ export default { ...@@ -24,12 +24,21 @@ export default {
default: '', default: '',
}, },
}, },
model: {
prop: 'visible',
event: 'change',
},
props: { props: {
modalId: { modalId: {
type: String, type: String,
required: false, required: false,
default: 'custom-notifications-modal', default: 'custom-notifications-modal',
}, },
visible: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -95,9 +104,11 @@ export default { ...@@ -95,9 +104,11 @@ export default {
<template> <template>
<gl-modal <gl-modal
ref="modal" ref="modal"
:visible="visible"
:modal-id="modalId" :modal-id="modalId"
:title="$options.i18n.customNotificationsModal.title" :title="$options.i18n.customNotificationsModal.title"
@show="onOpen" @show="onOpen"
v-on="$listeners"
> >
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
......
<script> <script>
import { import { GlDropdown, GlDropdownDivider, GlTooltipDirective } from '@gitlab/ui';
GlButtonGroup,
GlButton,
GlDropdown,
GlDropdownDivider,
GlTooltipDirective,
GlModalDirective,
} from '@gitlab/ui';
import Api from '~/api'; import Api from '~/api';
import { sprintf } from '~/locale'; import { sprintf } from '~/locale';
import { CUSTOM_LEVEL, i18n } from '../constants'; import { CUSTOM_LEVEL, i18n } from '../constants';
...@@ -16,8 +9,6 @@ import NotificationsDropdownItem from './notifications_dropdown_item.vue'; ...@@ -16,8 +9,6 @@ import NotificationsDropdownItem from './notifications_dropdown_item.vue';
export default { export default {
name: 'NotificationsDropdown', name: 'NotificationsDropdown',
components: { components: {
GlButtonGroup,
GlButton,
GlDropdown, GlDropdown,
GlDropdownDivider, GlDropdownDivider,
NotificationsDropdownItem, NotificationsDropdownItem,
...@@ -25,7 +16,6 @@ export default { ...@@ -25,7 +16,6 @@ export default {
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
'gl-modal': GlModalDirective,
}, },
inject: { inject: {
containerClass: { containerClass: {
...@@ -57,6 +47,7 @@ export default { ...@@ -57,6 +47,7 @@ export default {
return { return {
selectedNotificationLevel: this.initialNotificationLevel, selectedNotificationLevel: this.initialNotificationLevel,
isLoading: false, isLoading: false,
notificationsModalVisible: false,
}; };
}, },
computed: { computed: {
...@@ -95,6 +86,11 @@ export default { ...@@ -95,6 +86,11 @@ export default {
}, },
}, },
methods: { methods: {
openNotificationsModal() {
if (this.isCustomNotification) {
this.notificationsModalVisible = true;
}
},
selectItem(level) { selectItem(level) {
if (level !== this.selectedNotificationLevel) { if (level !== this.selectedNotificationLevel) {
this.updateNotificationLevel(level); this.updateNotificationLevel(level);
...@@ -106,10 +102,7 @@ export default { ...@@ -106,10 +102,7 @@ export default {
try { try {
await Api.updateNotificationSettings(this.projectId, this.groupId, { level }); await Api.updateNotificationSettings(this.projectId, this.groupId, { level });
this.selectedNotificationLevel = level; this.selectedNotificationLevel = level;
this.openNotificationsModal();
if (level === CUSTOM_LEVEL) {
this.$refs.customNotificationsModal.open();
}
} catch (error) { } catch (error) {
this.$toast.show(this.$options.i18n.updateNotificationLevelErrorMessage, { type: 'error' }); this.$toast.show(this.$options.i18n.updateNotificationLevelErrorMessage, { type: 'error' });
} finally { } finally {
...@@ -125,54 +118,16 @@ export default { ...@@ -125,54 +118,16 @@ export default {
<template> <template>
<div :class="containerClass"> <div :class="containerClass">
<gl-button-group
v-if="isCustomNotification"
v-gl-tooltip="{ title: buttonTooltip }"
data-testid="notification-button"
:class="{ disabled: disabled }"
:size="buttonSize"
>
<gl-button
v-gl-modal="$options.modalId"
:size="buttonSize"
:icon="buttonIcon"
:loading="isLoading"
:disabled="disabled"
>
<template v-if="buttonText">{{ buttonText }}</template>
</gl-button>
<gl-dropdown :size="buttonSize" :disabled="disabled">
<notifications-dropdown-item
v-for="item in notificationLevels"
:key="item.level"
:level="item.level"
:title="item.title"
:description="item.description"
:notification-level="selectedNotificationLevel"
@item-selected="selectItem"
/>
<gl-dropdown-divider />
<notifications-dropdown-item
:key="$options.customLevel"
:level="$options.customLevel"
:title="$options.i18n.notificationTitles.custom"
:description="$options.i18n.notificationDescriptions.custom"
:notification-level="selectedNotificationLevel"
@item-selected="selectItem"
/>
</gl-dropdown>
</gl-button-group>
<gl-dropdown <gl-dropdown
v-else
v-gl-tooltip="{ title: buttonTooltip }" v-gl-tooltip="{ title: buttonTooltip }"
data-testid="notification-button" data-testid="notification-dropdown"
:text="buttonText" :size="buttonSize"
:icon="buttonIcon" :icon="buttonIcon"
:loading="isLoading" :loading="isLoading"
:size="buttonSize"
:disabled="disabled" :disabled="disabled"
:class="{ disabled: disabled }" :split="isCustomNotification"
:text="buttonText"
@click="openNotificationsModal"
> >
<notifications-dropdown-item <notifications-dropdown-item
v-for="item in notificationLevels" v-for="item in notificationLevels"
...@@ -193,6 +148,6 @@ export default { ...@@ -193,6 +148,6 @@ export default {
@item-selected="selectItem" @item-selected="selectItem"
/> />
</gl-dropdown> </gl-dropdown>
<custom-notifications-modal ref="customNotificationsModal" :modal-id="$options.modalId" /> <custom-notifications-modal v-model="notificationsModalVisible" :modal-id="$options.modalId" />
</div> </div>
</template> </template>
---
title: Simplify notifications dropdown
merge_request: 55522
author:
type: changed
...@@ -170,14 +170,14 @@ RSpec.describe 'Group show page' do ...@@ -170,14 +170,14 @@ RSpec.describe 'Group show page' do
it 'is enabled by default' do it 'is enabled by default' do
visit path visit path
expect(page).to have_selector('[data-testid="notification-button"]:not(.disabled)') expect(page).to have_selector('[data-testid="notification-dropdown"] button:not(.disabled)')
end end
it 'is disabled if emails are disabled' do it 'is disabled if emails are disabled' do
group.update_attribute(:emails_disabled, true) group.update_attribute(:emails_disabled, true)
visit path visit path
expect(page).to have_selector('[data-testid="notification-button"].disabled') expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled')
end end
end end
......
...@@ -15,17 +15,17 @@ RSpec.describe 'User visits the notifications tab', :js do ...@@ -15,17 +15,17 @@ RSpec.describe 'User visits the notifications tab', :js do
it 'changes the project notifications setting' do it 'changes the project notifications setting' do
expect(page).to have_content('Notifications') expect(page).to have_content('Notifications')
first('[data-testid="notification-button"]').click first('[data-testid="notification-dropdown"]').click
click_button('On mention') click_button('On mention')
expect(page).to have_selector('[data-testid="notification-button"]', text: 'On mention') expect(page).to have_selector('[data-testid="notification-dropdown"]', text: 'On mention')
end end
context 'when project emails are disabled' do context 'when project emails are disabled' do
let(:project) { create(:project, emails_disabled: true) } let(:project) { create(:project, emails_disabled: true) }
it 'notification button is disabled' do it 'notification button is disabled' do
expect(page).to have_selector('[data-testid="notification-button"].disabled') expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled')
end end
end end
end end
...@@ -10,7 +10,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do ...@@ -10,7 +10,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
end end
def click_notifications_button def click_notifications_button
first('[data-testid="notification-button"]').click first('[data-testid="notification-dropdown"]').click
end end
it 'changes the notification setting' do it 'changes the notification setting' do
...@@ -22,7 +22,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do ...@@ -22,7 +22,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button click_notifications_button
page.within first('[data-testid="notification-button"]') do page.within first('[data-testid="notification-dropdown"]') do
expect(page.find('.gl-new-dropdown-item.is-active')).to have_content('On mention') expect(page.find('.gl-new-dropdown-item.is-active')).to have_content('On mention')
expect(page).to have_css('[data-testid="notifications-icon"]') expect(page).to have_css('[data-testid="notifications-icon"]')
end end
...@@ -33,7 +33,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do ...@@ -33,7 +33,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button click_notifications_button
click_button 'Disabled' click_button 'Disabled'
page.within first('[data-testid="notification-button"]') do page.within first('[data-testid="notification-dropdown"]') do
expect(page).to have_css('[data-testid="notifications-off-icon"]') expect(page).to have_css('[data-testid="notifications-off-icon"]')
end end
end end
...@@ -80,7 +80,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do ...@@ -80,7 +80,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
it 'is disabled' do it 'is disabled' do
visit project_path(project) visit project_path(project)
expect(page).to have_selector('[data-testid="notification-button"].disabled', visible: true) expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled', visible: true)
end end
end end
end end
import { GlButtonGroup, GlButton, GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
...@@ -15,14 +15,10 @@ const mockToastShow = jest.fn(); ...@@ -15,14 +15,10 @@ const mockToastShow = jest.fn();
describe('NotificationsDropdown', () => { describe('NotificationsDropdown', () => {
let wrapper; let wrapper;
let mockAxios; let mockAxios;
let glModalDirective;
function createComponent(injectedProperties = {}) { function createComponent(injectedProperties = {}) {
glModalDirective = jest.fn();
return shallowMount(NotificationsDropdown, { return shallowMount(NotificationsDropdown, {
stubs: { stubs: {
GlButtonGroup,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
NotificationsDropdownItem, NotificationsDropdownItem,
...@@ -30,11 +26,6 @@ describe('NotificationsDropdown', () => { ...@@ -30,11 +26,6 @@ describe('NotificationsDropdown', () => {
}, },
directives: { directives: {
GlTooltip: createMockDirective(), GlTooltip: createMockDirective(),
glModal: {
bind(_, { value }) {
glModalDirective(value);
},
},
}, },
provide: { provide: {
dropdownItems: mockDropdownItems, dropdownItems: mockDropdownItems,
...@@ -49,13 +40,12 @@ describe('NotificationsDropdown', () => { ...@@ -49,13 +40,12 @@ describe('NotificationsDropdown', () => {
}); });
} }
const findButtonGroup = () => wrapper.find(GlButtonGroup);
const findButton = () => wrapper.find(GlButton);
const findDropdown = () => wrapper.find(GlDropdown); const findDropdown = () => wrapper.find(GlDropdown);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`); const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findAllNotificationsDropdownItems = () => wrapper.findAll(NotificationsDropdownItem); const findAllNotificationsDropdownItems = () => wrapper.findAll(NotificationsDropdownItem);
const findDropdownItemAt = (index) => const findDropdownItemAt = (index) =>
findAllNotificationsDropdownItems().at(index).find(GlDropdownItem); findAllNotificationsDropdownItems().at(index).find(GlDropdownItem);
const findNotificationsModal = () => wrapper.find(CustomNotificationsModal);
const clickDropdownItemAt = async (index) => { const clickDropdownItemAt = async (index) => {
const dropdownItem = findDropdownItemAt(index); const dropdownItem = findDropdownItemAt(index);
...@@ -83,8 +73,8 @@ describe('NotificationsDropdown', () => { ...@@ -83,8 +73,8 @@ describe('NotificationsDropdown', () => {
}); });
}); });
it('renders a button group', () => { it('renders split dropdown', () => {
expect(findButtonGroup().exists()).toBe(true); expect(findDropdown().props().split).toBe(true);
}); });
it('shows the button text when showLabel is true', () => { it('shows the button text when showLabel is true', () => {
...@@ -93,7 +83,7 @@ describe('NotificationsDropdown', () => { ...@@ -93,7 +83,7 @@ describe('NotificationsDropdown', () => {
showLabel: true, showLabel: true,
}); });
expect(findButton().text()).toBe('Custom'); expect(findDropdown().props().text).toBe('Custom');
}); });
it("doesn't show the button text when showLabel is false", () => { it("doesn't show the button text when showLabel is false", () => {
...@@ -102,7 +92,7 @@ describe('NotificationsDropdown', () => { ...@@ -102,7 +92,7 @@ describe('NotificationsDropdown', () => {
showLabel: false, showLabel: false,
}); });
expect(findButton().text()).toBe(''); expect(findDropdown().props().text).toBe(null);
}); });
it('opens the modal when the user clicks the button', async () => { it('opens the modal when the user clicks the button', async () => {
...@@ -113,9 +103,9 @@ describe('NotificationsDropdown', () => { ...@@ -113,9 +103,9 @@ describe('NotificationsDropdown', () => {
initialNotificationLevel: 'custom', initialNotificationLevel: 'custom',
}); });
findButton().vm.$emit('click'); await findDropdown().vm.$emit('click');
expect(glModalDirective).toHaveBeenCalled(); expect(findNotificationsModal().props().visible).toBe(true);
}); });
}); });
...@@ -126,8 +116,8 @@ describe('NotificationsDropdown', () => { ...@@ -126,8 +116,8 @@ describe('NotificationsDropdown', () => {
}); });
}); });
it('does not render a button group', () => { it('renders unified dropdown', () => {
expect(findButtonGroup().exists()).toBe(false); expect(findDropdown().props().split).toBe(false);
}); });
it('shows the button text when showLabel is true', () => { it('shows the button text when showLabel is true', () => {
...@@ -162,7 +152,7 @@ describe('NotificationsDropdown', () => { ...@@ -162,7 +152,7 @@ describe('NotificationsDropdown', () => {
initialNotificationLevel: level, initialNotificationLevel: level,
}); });
const tooltipElement = findByTestId('notification-button'); const tooltipElement = findByTestId('notification-dropdown');
const tooltip = getBinding(tooltipElement.element, 'gl-tooltip'); const tooltip = getBinding(tooltipElement.element, 'gl-tooltip');
expect(tooltip.value.title).toBe(`${tooltipTitlePrefix} - ${title}`); expect(tooltip.value.title).toBe(`${tooltipTitlePrefix} - ${title}`);
...@@ -264,11 +254,9 @@ describe('NotificationsDropdown', () => { ...@@ -264,11 +254,9 @@ describe('NotificationsDropdown', () => {
mockAxios.onPut('/api/v4/notification_settings').reply(httpStatus.OK, {}); mockAxios.onPut('/api/v4/notification_settings').reply(httpStatus.OK, {});
wrapper = createComponent(); wrapper = createComponent();
const mockModalShow = jest.spyOn(wrapper.vm.$refs.customNotificationsModal, 'open');
await clickDropdownItemAt(5); await clickDropdownItemAt(5);
expect(mockModalShow).toHaveBeenCalled(); expect(findNotificationsModal().props().visible).toBe(true);
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment