Commit c0ccea30 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '351915-experiment-add-video-tutorials-in-continuous-onboarding' into 'master'

Create the add videos to continuous onboarding experiment

See merge request gitlab-org/gitlab!82274
parents 8b80686d 06795881
...@@ -127,8 +127,12 @@ export default { ...@@ -127,8 +127,12 @@ export default {
</p> </p>
<gl-progress-bar :value="progressValue" :max="$options.maxValue" /> <gl-progress-bar :value="progressValue" :max="$options.maxValue" />
</div> </div>
<div class="row row-cols-1 row-cols-md-3 gl-mt-5"> <div class="row">
<div v-for="section in $options.actionSections" :key="section" class="col gl-mb-6"> <div
v-for="section in $options.actionSections"
:key="section"
class="gl-mt-5 col-sm-12 col-mb-6 col-lg-4"
>
<learn-gitlab-section-card <learn-gitlab-section-card
:section="section" :section="section"
:svg="svgFor(section)" :svg="svgFor(section)"
......
...@@ -34,17 +34,23 @@ export default { ...@@ -34,17 +34,23 @@ export default {
}; };
</script> </script>
<template> <template>
<gl-card class="gl-pt-0 learn-gitlab-section-card"> <gl-card
<div class="learn-gitlab-section-card-header"> class="gl-pt-0 h-100"
header-class="gl-bg-white gl-border-0 gl-pb-0"
body-class="gl-pt-0"
>
<template #header>
<img :src="svg" /> <img :src="svg" />
<h2 class="gl-font-lg gl-mb-3">{{ $options.i18n[section].title }}</h2> <h2 class="gl-font-lg gl-mb-3">{{ $options.i18n[section].title }}</h2>
<p class="gl-text-gray-700 gl-mb-6">{{ $options.i18n[section].description }}</p> <p class="gl-text-gray-700 gl-mb-6">{{ $options.i18n[section].description }}</p>
</div> </template>
<learn-gitlab-section-link <template #default>
v-for="[action, value] in sortedActions" <learn-gitlab-section-link
:key="action" v-for="[action, value] in sortedActions"
:action="action" :key="action"
:value="value" :action="action"
/> :value="value"
/>
</template>
</gl-card> </gl-card>
</template> </template>
<script> <script>
import { GlLink, GlIcon } from '@gitlab/ui'; import { GlLink, GlIcon, GlButton, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
import { isExperimentVariant } from '~/experimentation/utils'; import { isExperimentVariant } from '~/experimentation/utils';
import eventHub from '~/invite_members/event_hub'; import eventHub from '~/invite_members/event_hub';
import { s__ } from '~/locale'; import { s__, __ } from '~/locale';
import { ACTION_LABELS } from '../constants'; import { ACTION_LABELS } from '../constants';
export default { export default {
name: 'LearnGitlabSectionLink', name: 'LearnGitlabSectionLink',
components: { GlLink, GlIcon }, components: {
GlLink,
GlIcon,
GlButton,
GitlabExperiment,
},
directives: {
GlTooltip,
},
i18n: { i18n: {
ACTION_LABELS,
trialOnly: s__('LearnGitlab|Trial only'), trialOnly: s__('LearnGitlab|Trial only'),
watchHow: __('Watch how'),
}, },
props: { props: {
action: { action: {
...@@ -23,6 +32,9 @@ export default { ...@@ -23,6 +32,9 @@ export default {
}, },
}, },
computed: { computed: {
linkTitle() {
return ACTION_LABELS[this.action].title;
},
trialOnly() { trialOnly() {
return ACTION_LABELS[this.action].trialRequired; return ACTION_LABELS[this.action].trialRequired;
}, },
...@@ -34,6 +46,9 @@ export default { ...@@ -34,6 +46,9 @@ export default {
openInNewTab() { openInNewTab() {
return ACTION_LABELS[this.action]?.openInNewTab === true || this.value.openInNewTab === true; return ACTION_LABELS[this.action]?.openInNewTab === true || this.value.openInNewTab === true;
}, },
linkToVideoTutorial() {
return ACTION_LABELS[this.action].videoTutorial;
},
}, },
methods: { methods: {
openModal() { openModal() {
...@@ -44,32 +59,54 @@ export default { ...@@ -44,32 +59,54 @@ export default {
</script> </script>
<template> <template>
<div class="gl-mb-4"> <div class="gl-mb-4">
<span v-if="value.completed" class="gl-text-green-500"> <div v-if="trialOnly" class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only">
<gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" /> {{ $options.i18n.trialOnly }}
{{ $options.i18n.ACTION_LABELS[action].title }} </div>
</span> <div class="flex align-items-center">
<gl-link <span v-if="value.completed" class="gl-text-green-500">
v-else-if="showInviteModalLink" <gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" />
data-track-action="click_link" {{ linkTitle }}
:data-track-label="$options.i18n.ACTION_LABELS[action].title" </span>
data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding" <gl-link
data-testid="invite-for-help-continuous-onboarding-experiment-link" v-else-if="showInviteModalLink"
@click="openModal" data-track-action="click_link"
> :data-track-label="linkTitle"
{{ $options.i18n.ACTION_LABELS[action].title }} data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding"
</gl-link> data-testid="invite-for-help-continuous-onboarding-experiment-link"
<gl-link @click="openModal"
v-else >
:target="openInNewTab ? '_blank' : '_self'" {{ linkTitle }}
:href="value.url" </gl-link>
data-testid="uncompleted-learn-gitlab-link" <gl-link
data-track-action="click_link" v-else
:data-track-label="$options.i18n.ACTION_LABELS[action].title" :target="openInNewTab ? '_blank' : '_self'"
> :href="value.url"
{{ $options.i18n.ACTION_LABELS[action].title }} data-testid="uncompleted-learn-gitlab-link"
</gl-link> data-track-action="click_link"
<span v-if="trialOnly" class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only"> :data-track-label="linkTitle"
- {{ $options.i18n.trialOnly }} >
</span> {{ linkTitle }}
</gl-link>
<gitlab-experiment name="video_tutorials_continuous_onboarding">
<template #control></template>
<template #candidate>
<gl-button
v-if="linkToVideoTutorial"
v-gl-tooltip
category="tertiary"
icon="live-preview"
:title="$options.i18n.watchHow"
:aria-label="$options.i18n.watchHow"
:href="linkToVideoTutorial"
target="_blank"
class="ml-auto"
data-testid="video-tutorial-link"
data-track-action="click_video_link"
:data-track-label="linkTitle"
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
/>
</template>
</gitlab-experiment>
</div>
</div> </div>
</template> </template>
...@@ -40,6 +40,7 @@ export const ACTION_LABELS = { ...@@ -40,6 +40,7 @@ export const ACTION_LABELS = {
trialRequired: true, trialRequired: true,
section: 'workspace', section: 'workspace',
position: 4, position: 4,
videoTutorial: 'https://vimeo.com/670896787',
}, },
requiredMrApprovalsEnabled: { requiredMrApprovalsEnabled: {
title: s__('LearnGitLab|Add merge request approval'), title: s__('LearnGitLab|Add merge request approval'),
...@@ -48,6 +49,7 @@ export const ACTION_LABELS = { ...@@ -48,6 +49,7 @@ export const ACTION_LABELS = {
trialRequired: true, trialRequired: true,
section: 'workspace', section: 'workspace',
position: 5, position: 5,
videoTutorial: 'https://vimeo.com/670904904',
}, },
mergeRequestCreated: { mergeRequestCreated: {
title: s__('LearnGitLab|Submit a merge request'), title: s__('LearnGitLab|Submit a merge request'),
......
.learn-gitlab-info-card-content { .learn-gitlab-info-card-content {
height: 200px; height: 200px;
} }
.learn-gitlab-section-card {
height: 400px;
}
.learn-gitlab-section-card-header {
height: 165px;
}
...@@ -4,6 +4,7 @@ class Projects::LearnGitlabController < Projects::ApplicationController ...@@ -4,6 +4,7 @@ class Projects::LearnGitlabController < Projects::ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :check_experiment_enabled? before_action :check_experiment_enabled?
before_action :enable_invite_for_help_continuous_onboarding_experiment before_action :enable_invite_for_help_continuous_onboarding_experiment
before_action :enable_video_tutorials_continuous_onboarding_experiment
feature_category :users feature_category :users
...@@ -24,4 +25,8 @@ class Projects::LearnGitlabController < Projects::ApplicationController ...@@ -24,4 +25,8 @@ class Projects::LearnGitlabController < Projects::ApplicationController
e.publish_to_database e.publish_to_database
end end
end end
def enable_video_tutorials_continuous_onboarding_experiment
experiment(:video_tutorials_continuous_onboarding, namespace: project&.namespace).publish
end
end end
# frozen_string_literal: true
class VideoTutorialsContinuousOnboardingExperiment < ApplicationExperiment
control { }
candidate { }
end
---
name: video_tutorials_continuous_onboarding
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82274
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351916
milestone: '14.9'
type: experiment
group: group::adoption
default_enabled: false
...@@ -41559,6 +41559,9 @@ msgstr "" ...@@ -41559,6 +41559,9 @@ msgstr ""
msgid "Warning: Synchronizing LDAP removes direct members' access." msgid "Warning: Synchronizing LDAP removes direct members' access."
msgstr "" msgstr ""
msgid "Watch how"
msgstr ""
msgid "We are currently unable to fetch data for the pipeline header." msgid "We are currently unable to fetch data for the pipeline header."
msgstr "" msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe VideoTutorialsContinuousOnboardingExperiment do
it "defines a control and candidate" do
expect(subject.behaviors.keys).to match_array(%w[control candidate])
end
end
...@@ -2,31 +2,26 @@ ...@@ -2,31 +2,26 @@
exports[`Learn GitLab Section Card renders correctly 1`] = ` exports[`Learn GitLab Section Card renders correctly 1`] = `
<gl-card-stub <gl-card-stub
bodyclass="" bodyclass="gl-pt-0"
class="gl-pt-0 learn-gitlab-section-card" class="gl-pt-0 h-100"
footerclass="" footerclass=""
headerclass="" headerclass="gl-bg-white gl-border-0 gl-pb-0"
> >
<div <img
class="learn-gitlab-section-card-header" src="workspace.svg"
/>
<h2
class="gl-font-lg gl-mb-3"
> >
<img Set up your workspace
src="workspace.svg" </h2>
/>
<h2
class="gl-font-lg gl-mb-3"
>
Set up your workspace
</h2>
<p
class="gl-text-gray-700 gl-mb-6"
>
Complete these tasks first so you can enjoy GitLab's features to their fullest:
</p>
</div>
<p
class="gl-text-gray-700 gl-mb-6"
>
Complete these tasks first so you can enjoy GitLab's features to their fullest:
</p>
<learn-gitlab-section-link-stub <learn-gitlab-section-link-stub
action="userAdded" action="userAdded"
value="[object Object]" value="[object Object]"
......
import { shallowMount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { stubExperiments } from 'helpers/experimentation_helper'; import { stubExperiments } from 'helpers/experimentation_helper';
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper'; import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
import eventHub from '~/invite_members/event_hub'; import eventHub from '~/invite_members/event_hub';
...@@ -26,7 +26,7 @@ describe('Learn GitLab Section Link', () => { ...@@ -26,7 +26,7 @@ describe('Learn GitLab Section Link', () => {
}); });
const createWrapper = (action = defaultAction, props = {}) => { const createWrapper = (action = defaultAction, props = {}) => {
wrapper = shallowMount(LearnGitlabSectionLink, { wrapper = mount(LearnGitlabSectionLink, {
propsData: { action, value: { ...defaultProps, ...props } }, propsData: { action, value: { ...defaultProps, ...props } },
}); });
}; };
...@@ -36,6 +36,8 @@ describe('Learn GitLab Section Link', () => { ...@@ -36,6 +36,8 @@ describe('Learn GitLab Section Link', () => {
const findUncompletedLink = () => wrapper.find('[data-testid="uncompleted-learn-gitlab-link"]'); const findUncompletedLink = () => wrapper.find('[data-testid="uncompleted-learn-gitlab-link"]');
const videoTutorialLink = () => wrapper.find('[data-testid="video-tutorial-link"]');
it('renders no icon when not completed', () => { it('renders no icon when not completed', () => {
createWrapper(undefined, { completed: false }); createWrapper(undefined, { completed: false });
...@@ -130,4 +132,44 @@ describe('Learn GitLab Section Link', () => { ...@@ -130,4 +132,44 @@ describe('Learn GitLab Section Link', () => {
unmockTracking(); unmockTracking();
}); });
}); });
describe('video_tutorials_continuous_onboarding experiment', () => {
describe('when control', () => {
beforeEach(() => {
stubExperiments({ video_tutorials_continuous_onboarding: 'control' });
createWrapper('codeOwnersEnabled');
});
it('renders no video link', () => {
expect(videoTutorialLink().exists()).toBe(false);
});
});
describe('when candidate', () => {
beforeEach(() => {
stubExperiments({ video_tutorials_continuous_onboarding: 'candidate' });
createWrapper('codeOwnersEnabled');
});
it('renders video link with blank target', () => {
const videoLinkElement = videoTutorialLink();
expect(videoLinkElement.exists()).toBe(true);
expect(videoLinkElement.attributes('target')).toEqual('_blank');
});
it('tracks the click', () => {
const trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
videoTutorialLink().trigger('click');
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_video_link', {
label: 'Add code owners',
property: 'Growth::Conversion::Experiment::LearnGitLab',
});
unmockTracking();
});
});
});
}); });
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