Commit ee0e6db9 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '225858-add-banner-to-homepage' into 'master'

Add banner to homepage for customization help

See merge request gitlab-org/gitlab!39348
parents fe915758 859283c7
<script>
import { GlBanner } from '@gitlab/ui';
import { s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
export default {
components: {
GlBanner,
},
inject: {
svgPath: {
default: '',
},
preferencesBehaviorPath: {
default: '',
},
calloutsPath: {
default: '',
},
calloutsFeatureId: {
default: '',
},
},
i18n: {
title: s__('CustomizeHomepageBanner|Do you want to customize this page?'),
body: s__(
'CustomizeHomepageBanner|This page shows a list of your projects by default but it can be changed to show projects\' activity, groups, your to-do list, assigned issues, assigned merge requests, and more. You can change this under "Homepage content" in your preferences',
),
button_text: s__('CustomizeHomepageBanner|Go to preferences'),
},
data() {
return {
visible: true,
};
},
methods: {
handleClose() {
axios
.post(this.calloutsPath, {
feature_name: this.calloutsFeatureId,
})
.catch(e => {
// eslint-disable-next-line @gitlab/require-i18n-strings, no-console
console.error('Failed to dismiss banner.', e);
});
this.visible = false;
},
},
};
</script>
<template>
<gl-banner
v-if="visible"
:title="$options.i18n.title"
:button-text="$options.i18n.button_text"
:button-link="preferencesBehaviorPath"
:svg-path="svgPath"
@close="handleClose"
>
<p>
{{ $options.i18n.body }}
</p>
</gl-banner>
</template>
import ProjectsList from '~/projects_list';
import initCustomizeHomepageBanner from './init_customize_homepage_banner';
document.addEventListener('DOMContentLoaded', () => {
new ProjectsList(); // eslint-disable-line no-new
initCustomizeHomepageBanner();
});
import Vue from 'vue';
import CustomizeHomepageBanner from './components/customize_homepage_banner.vue';
export default () => {
const el = document.querySelector('.js-customize-homepage-banner');
if (!el) {
return false;
}
return new Vue({
el,
provide: { ...el.dataset },
render: createElement => createElement(CustomizeHomepageBanner),
});
};
......@@ -13,6 +13,7 @@ class RootController < Dashboard::ProjectsController
before_action :redirect_unlogged_user, if: -> { current_user.nil? }
before_action :redirect_logged_user, if: -> { current_user.present? }
before_action :customize_homepage, only: :index, if: -> { current_user.present? }
# We only need to load the projects when the user is logged in but did not
# configure a dashboard. In which case we render projects. We can do that straight
# from the #index action.
......@@ -66,6 +67,10 @@ class RootController < Dashboard::ProjectsController
root_urls.exclude?(home_page_url)
end
def customize_homepage
@customize_homepage = experiment_enabled?(:customize_homepage)
end
end
RootController.prepend_if_ee('EE::RootController')
......@@ -7,6 +7,7 @@ module UserCalloutsHelper
SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed'
TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight'
WEBHOOKS_MOVED = 'webhooks_moved'
CUSTOMIZE_HOMEPAGE = 'customize_homepage'
def show_admin_integrations_moved?
!user_dismissed?(ADMIN_INTEGRATIONS_MOVED)
......@@ -44,6 +45,10 @@ module UserCalloutsHelper
!user_dismissed?(WEBHOOKS_MOVED)
end
def show_customize_homepage_banner?(customize_homepage)
customize_homepage && !user_dismissed?(CUSTOMIZE_HOMEPAGE)
end
private
def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil)
......
......@@ -19,7 +19,8 @@ module UserCalloutEnums
webhooks_moved: 13,
admin_integrations_moved: 15,
personal_access_token_expiry: 21, # EE-only
suggest_pipeline: 22
suggest_pipeline: 22,
customize_homepage: 23
}
end
end
......
......@@ -3,6 +3,14 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
- if show_customize_homepage_banner?(@customize_homepage)
= content_for :customize_homepage_banner do
.d-none.d-md-block{ class: "gl-pt-6! gl-pb-2! #{(container_class unless @no_container)} #{@content_class}" }
.js-customize-homepage-banner{ data: { svg_path: image_path('illustrations/monitoring/getting_started.svg'),
preferences_behavior_path: profile_preferences_path(anchor: 'behavior'),
callouts_path: user_callouts_path,
callouts_feature_id: UserCalloutsHelper::CUSTOMIZE_HOMEPAGE } }
= render_dashboard_gold_trial(current_user)
- page_title _("Projects")
......
......@@ -16,6 +16,7 @@
= render_account_recovery_regular_check
= render_if_exists "layouts/header/ee_subscribable_banner"
= render_if_exists "shared/namespace_storage_limit_alert"
= yield :customize_homepage_banner
- unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs"
.d-flex
......
......@@ -59,6 +59,9 @@ module Gitlab
},
contact_sales_btn_in_app: {
tracking_category: 'Growth::Conversion::Experiment::ContactSalesInApp'
},
customize_homepage: {
tracking_category: 'Growth::Expansion::Experiment::CustomizeHomepage'
}
}.freeze
......
......@@ -7429,6 +7429,15 @@ msgstr ""
msgid "Customize your pipeline configuration."
msgstr ""
msgid "CustomizeHomepageBanner|Do you want to customize this page?"
msgstr ""
msgid "CustomizeHomepageBanner|Go to preferences"
msgstr ""
msgid "CustomizeHomepageBanner|This page shows a list of your projects by default but it can be changed to show projects' activity, groups, your to-do list, assigned issues, assigned merge requests, and more. You can change this under \"Homepage content\" in your preferences"
msgstr ""
msgid "Cycle Time"
msgstr ""
......
......@@ -122,6 +122,30 @@ RSpec.describe RootController do
expect(response).to render_template 'dashboard/projects/index'
end
context 'when experiment is enabled' do
before do
stub_experiment_for_user(customize_homepage: true)
end
it 'renders the default dashboard' do
get :index
expect(assigns[:customize_homepage]).to be true
end
end
context 'when experiment not enabled' do
before do
stub_experiment(customize_homepage: false)
end
it 'renders the default dashboard' do
get :index
expect(assigns[:customize_homepage]).to be false
end
end
end
end
end
......
import { shallowMount } from '@vue/test-utils';
import CustomizeHomepageBanner from '~/pages/dashboard/projects/index/components/customize_homepage_banner.vue';
import { GlBanner } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
const svgPath = '/illustrations/background';
const provide = {
svgPath,
preferencesBehaviorPath: 'some/behavior/path',
calloutsPath: 'call/out/path',
calloutsFeatureId: 'some-feature-id',
};
const createComponent = () => {
return shallowMount(CustomizeHomepageBanner, { provide });
};
describe('CustomizeHomepageBanner', () => {
let mockAxios;
let wrapper;
beforeEach(() => {
mockAxios = new MockAdapter(axios);
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockAxios.restore();
});
it('should render the banner when not dismissed', () => {
expect(wrapper.contains(GlBanner)).toBe(true);
});
it('should close the banner when dismiss is clicked', async () => {
mockAxios.onPost(provide.calloutsPath).replyOnce(200);
expect(wrapper.contains(GlBanner)).toBe(true);
wrapper.find(GlBanner).vm.$emit('close');
await wrapper.vm.$nextTick();
expect(wrapper.contains(GlBanner)).toBe(false);
});
it('includes the body text from options', () => {
expect(wrapper.html()).toContain(wrapper.vm.$options.i18n.body);
});
});
......@@ -81,6 +81,36 @@ RSpec.describe UserCalloutsHelper do
end
end
describe '.show_customize_homepage_banner?' do
let(:customize_homepage) { true }
subject { helper.show_customize_homepage_banner?(customize_homepage) }
context 'when user has not dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::CUSTOMIZE_HOMEPAGE) { false }
end
context 'when customize_homepage is set' do
it { is_expected.to be true }
end
context 'when customize_homepage is false' do
let(:customize_homepage) { false }
it { is_expected.to be false }
end
end
context 'when user dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::CUSTOMIZE_HOMEPAGE) { true }
end
it { is_expected.to be false }
end
end
describe '.render_flash_user_callout' do
it 'renders the flash_user_callout partial' do
expect(helper).to receive(:render)
......
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