Commit 2c9b3469 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '224185-remember-when-user-closes-mr-approvals-promo' into 'master'

XP: Remember when user collapses the MR Approvals promo

See merge request gitlab-org/gitlab!76807
parents aff59420 ed696bd1
......@@ -17,6 +17,7 @@ export const BV_HIDE_MODAL = 'bv::hide::modal';
export const BV_HIDE_TOOLTIP = 'bv::hide::tooltip';
export const BV_DROPDOWN_SHOW = 'bv::dropdown::show';
export const BV_DROPDOWN_HIDE = 'bv::dropdown::hide';
export const BV_COLLAPSE_STATE = 'bv::collapse::state';
export const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!';
......
<script>
import { GlAccordion, GlAccordionItem, GlButton, GlLink } from '@gitlab/ui';
import { parseBoolean } from '~/lib/utils/common_utils';
import AccessorUtilities from '~/lib/utils/accessor';
import { BV_COLLAPSE_STATE } from '~/lib/utils/constants';
import { MR_APPROVALS_PROMO_DISMISSED, MR_APPROVALS_PROMO_I18N } from '../../constants';
const canUseLocalStorage = AccessorUtilities.canUseLocalStorage();
export default {
components: {
......@@ -9,41 +15,60 @@ export default {
GlLink,
},
inject: ['learnMorePath', 'promoImageAlt', 'promoImagePath', 'tryNowPath'],
data() {
return {
userManuallyCollapsed:
canUseLocalStorage && parseBoolean(localStorage.getItem(MR_APPROVALS_PROMO_DISMISSED)),
};
},
i18n: MR_APPROVALS_PROMO_I18N,
mounted() {
if (!this.userManuallyCollapsed) {
this.$root.$on(BV_COLLAPSE_STATE, this.collapseAccordionItem);
}
},
methods: {
collapseAccordionItem(_, state) {
if (state === false) {
// We only need to track that this happens at least once
this.$root.$off(BV_COLLAPSE_STATE, this.collapseAccordionItem);
this.userManuallyCollapsed = true;
if (canUseLocalStorage) {
localStorage.setItem(MR_APPROVALS_PROMO_DISMISSED, true);
}
}
},
},
};
</script>
<template>
<div class="gl-mt-2">
<p class="gl-mb-0 gl-text-gray-500">
{{ __('Approvals are optional.') }}
{{ $options.i18n.summary }}
</p>
<gl-accordion :header-level="3">
<gl-accordion-item :title="s__('ApprovalRule|Approval rules')" visible>
<gl-accordion-item :title="$options.i18n.accordionTitle" :visible="!userManuallyCollapsed">
<h4 class="gl-font-base gl-line-height-20 gl-mt-5 gl-mb-3">
{{ s__('ApprovalRule|Add required approvers to improve your code review process') }}
{{ $options.i18n.promoTitle }}
</h4>
<div class="gl-display-flex">
<div class="gl-flex-grow-1 gl-max-w-62 gl-mr-5">
<ul class="gl-list-style-position-inside gl-p-0 gl-mb-3">
<li>{{ s__('ApprovalRule|Assign approvers by area of expertise.') }}</li>
<li>{{ s__('ApprovalRule|Increase your organization’s code quality.') }}</li>
<li>{{ s__('ApprovalRule|Reduce the overall time to merge.') }}</li>
<li>
{{
s__(
'ApprovalRule|Let GitLab designate eligible approvers based on the files changed.',
)
}}
<li v-for="(statement, index) in $options.i18n.valueStatements" :key="index">
{{ statement }}
</li>
</ul>
<p>
<gl-link :href="learnMorePath" target="_blank">
{{ s__('ApprovalRule|Learn more about merge request approval.') }}
{{ $options.i18n.learnMore }}
</gl-link>
</p>
<gl-button category="primary" variant="confirm" :href="tryNowPath" target="_blank">{{
s__('ApprovalRule|Try it for free')
$options.i18n.tryNow
}}</gl-button>
</div>
<div class="gl-flex-grow-0 gl-w-full gl-max-w-26 gl-display-none gl-md-display-block">
......
......@@ -157,3 +157,18 @@ export const APPROVAL_VULNERABILITY_STATES = {
dismissed: s__('ApprovalRule|Dismissed'),
resolved: s__('ApprovalRule|Resolved'),
};
export const MR_APPROVALS_PROMO_DISMISSED = 'mr_approvals_promo.dismissed';
export const MR_APPROVALS_PROMO_I18N = {
accordionTitle: s__('ApprovalRule|Approval rules'),
learnMore: s__('ApprovalRule|Learn more about merge request approval.'),
promoTitle: s__('ApprovalRule|Add required approvers to improve your code review process'),
summary: __('Approvals are optional.'),
tryNow: s__('ApprovalRule|Try it for free'),
valueStatements: [
s__('ApprovalRule|Assign approvers by area of expertise.'),
s__('ApprovalRule|Increase your organization’s code quality.'),
s__('ApprovalRule|Reduce the overall time to merge.'),
s__('ApprovalRule|Let GitLab designate eligible approvers based on the files changed.'),
],
};
import { GlAccordionItem, GlButton, GlLink } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { __, s__ } from '~/locale';
import { BV_COLLAPSE_STATE } from '~/lib/utils/constants';
import { MR_APPROVALS_PROMO_I18N } from 'ee/approvals/constants';
import FreeTierPromo from 'ee/approvals/components/mr_edit/free_tier_promo.vue';
describe('PaidFeatureCalloutBadge component', () => {
......@@ -29,7 +30,7 @@ describe('PaidFeatureCalloutBadge component', () => {
describe('summary text', () => {
it('is rendered correctly', () => {
expect(wrapper.findByText(__('Approvals are optional.')).exists()).toBeTruthy();
expect(wrapper.findByText(MR_APPROVALS_PROMO_I18N.summary).exists()).toBeTruthy();
});
});
......@@ -41,7 +42,7 @@ describe('PaidFeatureCalloutBadge component', () => {
});
it('is given the expected title prop', () => {
expect(promoItem.props('title')).toBe(s__('ApprovalRule|Approval rules'));
expect(promoItem.props('title')).toBe(MR_APPROVALS_PROMO_I18N.accordionTitle);
});
it('starts expanded by default', () => {
......@@ -52,7 +53,7 @@ describe('PaidFeatureCalloutBadge component', () => {
describe('promo title', () => {
it('is rendered correctly', () => {
const promoTitle = wrapper.findByRole('heading', {
name: s__('ApprovalRule|Add required approvers to improve your code review process'),
name: MR_APPROVALS_PROMO_I18N.promoTitle,
});
expect(promoTitle.exists()).toBeTruthy();
......@@ -63,12 +64,7 @@ describe('PaidFeatureCalloutBadge component', () => {
it('contains the expected statements', () => {
const statementItemTexts = wrapper.findAllByRole('listitem').wrappers.map((li) => li.text());
expect(statementItemTexts).toEqual([
s__('ApprovalRule|Assign approvers by area of expertise.'),
s__('ApprovalRule|Increase your organization’s code quality.'),
s__('ApprovalRule|Reduce the overall time to merge.'),
s__('ApprovalRule|Let GitLab designate eligible approvers based on the files changed.'),
]);
expect(statementItemTexts).toEqual(MR_APPROVALS_PROMO_I18N.valueStatements);
});
});
......@@ -84,9 +80,7 @@ describe('PaidFeatureCalloutBadge component', () => {
});
it('has correct text', () => {
expect(learnMoreLink.text()).toBe(
s__('ApprovalRule|Learn more about merge request approval.'),
);
expect(learnMoreLink.text()).toBe(MR_APPROVALS_PROMO_I18N.learnMore);
});
});
......@@ -102,7 +96,7 @@ describe('PaidFeatureCalloutBadge component', () => {
});
it('has correct text', () => {
expect(tryNowBtn.text()).toBe(s__('ApprovalRule|Try it for free'));
expect(tryNowBtn.text()).toBe(MR_APPROVALS_PROMO_I18N.tryNow);
});
});
......@@ -113,4 +107,42 @@ describe('PaidFeatureCalloutBadge component', () => {
expect(promoImage.attributes('src')).toBe('/some-image.svg');
});
});
describe('user interactions', () => {
describe('when user does not interact with the promo', () => {
describe('and we render a second time', () => {
it('also starts expanded by default', () => {
const secondWrapper = createComponent();
const promoItem = secondWrapper.findComponent(GlAccordionItem);
expect(promoItem.props('visible')).toBeTruthy();
});
});
});
describe('when user collapses the promo', () => {
beforeEach(async () => {
await wrapper.vm.$root.$emit(BV_COLLAPSE_STATE, 'accordion-item-id', false);
});
afterEach(() => {
localStorage.clear();
});
it('reflects that state in the promo collapsible item', () => {
const promoItem = wrapper.findComponent(GlAccordionItem);
expect(promoItem.props('visible')).toBeFalsy();
});
describe('and we render a second time', () => {
it('starts collapsed by default', () => {
const secondWrapper = createComponent();
const promoItem = secondWrapper.findComponent(GlAccordionItem);
expect(promoItem.props('visible')).toBeFalsy();
});
});
});
});
});
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