Commit e3d05350 authored by Jackie Fraser's avatar Jackie Fraser Committed by Ezekiel Kigbo

Display invite banner on groups page

Adds the Invite Your Teammates banner to Group overview page
 - behind feature flag invite_your_teammates_banner_a
 - no dismissal functionality yet
parent 86668025
<script>
import { GlBanner } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
components: {
GlBanner,
},
inject: ['svgPath', 'inviteMembersPath'],
data() {
return {
visible: true,
};
},
methods: {
handleClose() {
this.visible = false;
},
},
i18n: {
title: s__('InviteMembersBanner|Collaborate with your team'),
body: s__(
"InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge.",
),
button_text: s__('InviteMembersBanner|Invite your colleagues'),
},
};
</script>
<template>
<gl-banner
v-if="visible"
ref="banner"
:title="$options.i18n.title"
:button-text="$options.i18n.button_text"
:svg-path="svgPath"
:button-link="inviteMembersPath"
@close="handleClose"
>
<p>{{ $options.i18n.body }}</p>
</gl-banner>
</template>
import Vue from 'vue';
import InviteMembersBanner from '~/groups/components/invite_members_banner.vue';
export default function initInviteMembersBanner() {
const el = document.querySelector('.js-group-invite-members-banner');
if (!el) {
return false;
}
const { svgPath, inviteMembersPath } = el.dataset;
return new Vue({
el,
provide: {
svgPath,
inviteMembersPath,
},
render: createElement => createElement(InviteMembersBanner),
});
}
......@@ -8,6 +8,7 @@ import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GroupTabs from './group_tabs';
import initInviteMembersBanner from '~/groups/init_invite_members_banner';
export default function initGroupDetails(actionName = 'show') {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
......@@ -27,4 +28,5 @@ export default function initGroupDetails(actionName = 'show') {
if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper);
}
initInviteMembersBanner();
}
......@@ -167,8 +167,23 @@ module GroupsHelper
@group.packages_feature_enabled?
end
def show_invite_banner?(group)
Feature.enabled?(:invite_your_teammates_banner_a, group) &&
can?(current_user, :admin_group, group) &&
!just_created? &&
!multiple_members?(group)
end
private
def just_created?
flash[:notice] =~ /successfully created/
end
def multiple_members?(group)
group.member_count > 1
end
def get_group_sidebar_links
links = [:overview, :group_members]
......
......@@ -2,6 +2,12 @@
- page_title _("Groups")
- @content_class = "limit-container-width" unless fluid_layout
- if show_invite_banner?(@group)
= content_for :group_invite_members_banner do
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
.js-group-invite-members-banner{ data: { svg_path: image_path('illustrations/merge_requests.svg'),
invite_members_path: group_group_members_path(@group) } }
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
......
......@@ -3,6 +3,7 @@
= render "layouts/nav/sidebar/#{nav}"
.content-wrapper{ class: "#{@content_wrapper_class}" }
.mobile-overlay
= yield :group_invite_members_banner
.alert-wrapper
= render 'shared/outdated_browser'
= render_if_exists "layouts/header/licensed_user_count_threshold"
......
---
name: invite_your_teammates_banner_a
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37658
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/231275
group: group::expansion
type: development
default_enabled: false
\ No newline at end of file
......@@ -13495,6 +13495,15 @@ msgstr ""
msgid "InviteEmail|to join the %{strong_start}%{project_or_group_name}%{strong_end}"
msgstr ""
msgid "InviteMembersBanner|Collaborate with your team"
msgstr ""
msgid "InviteMembersBanner|Invite your colleagues"
msgstr ""
msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge."
msgstr ""
msgid "Invited"
msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import { GlBanner } from '@gitlab/ui';
import InviteMembersBanner from '~/groups/components/invite_members_banner.vue';
const expectedTitle = 'Collaborate with your team';
const expectedBody =
"We noticed that you haven't invited anyone to this group. Invite your colleagues so you can discuss issues, collaborate on merge requests, and share your knowledge";
const expectedSvgPath = '/illustrations/background';
const expectedInviteMembersPath = 'groups/members';
const expectedButtonText = 'Invite your colleagues';
const createComponent = (stubs = {}) => {
return shallowMount(InviteMembersBanner, {
provide: {
svgPath: expectedSvgPath,
inviteMembersPath: expectedInviteMembersPath,
},
stubs,
});
};
describe('InviteMembersBanner', () => {
let wrapper;
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('rendering', () => {
const findBanner = () => {
return wrapper.find(GlBanner);
};
beforeEach(() => {
wrapper = createComponent();
});
it('uses the svgPath for the banner svgpath', () => {
expect(findBanner().attributes('svgpath')).toBe(expectedSvgPath);
});
it('uses the title from options for title', () => {
expect(findBanner().attributes('title')).toBe(expectedTitle);
});
it('includes the body text from options', () => {
expect(findBanner().html()).toContain(expectedBody);
});
it('uses the button_text text from options for buttontext', () => {
expect(findBanner().attributes('buttontext')).toBe(expectedButtonText);
});
it('uses the href from inviteMembersPath for buttonlink', () => {
expect(findBanner().attributes('buttonlink')).toBe(expectedInviteMembersPath);
});
});
describe('dismissing', () => {
const findButton = () => {
return wrapper.find('button');
};
const stubs = {
GlBanner,
};
it('sets visible to false', () => {
wrapper = createComponent(stubs);
findButton().trigger('click');
expect(wrapper.vm.visible).toBe(false);
});
});
});
......@@ -369,4 +369,48 @@ RSpec.describe GroupsHelper do
it { is_expected.to be_falsey }
end
end
describe '#show_invite_banner?' do
let_it_be(:current_user) { create(:user) }
let_it_be_with_refind(:group) { create(:group) }
let_it_be(:users) { [current_user, create(:user)] }
subject { helper.show_invite_banner?(group) }
before do
allow(helper).to receive(:current_user) { current_user }
allow(helper).to receive(:can?).with(current_user, :admin_group, group).and_return(can_admin_group)
stub_feature_flags(invite_your_teammates_banner_a: feature_enabled_flag)
users.take(group_members_count).each { |user| group.add_guest(user) }
end
using RSpec::Parameterized::TableSyntax
where(:feature_enabled_flag, :can_admin_group, :group_members_count, :expected_result) do
true | true | 1 | true
true | false | 1 | false
false | true | 1 | false
false | false | 1 | false
true | true | 2 | false
true | false | 2 | false
false | true | 2 | false
false | false | 2 | false
end
with_them do
context 'when the group was just created' do
before do
flash[:notice] = "Group #{group.name} was successfully created"
end
it { is_expected.to be_falsey }
end
context 'when no flash message' do
it 'returns the expected result' do
expect(subject).to eq(expected_result)
end
end
end
end
end
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