Skip to content
代码片段 群组 项目
未验证 提交 2029e0b1 编辑于 作者: Anna Vovchenko's avatar Anna Vovchenko 提交者: GitLab
浏览文件

Merge branch 'approval-rule-promo-migration' into 'master'

No related branches found
No related tags found
无相关合并请求
...@@ -99,7 +99,6 @@ export default { ...@@ -99,7 +99,6 @@ export default {
> >
<template v-if="canAddApprovalRule" #actions> <template v-if="canAddApprovalRule" #actions>
<gl-button <gl-button
:class="{ 'gl-mr-3': targetBranch, 'gl-mr-0': !targetBranch }"
:disabled="isLoading" :disabled="isLoading"
category="secondary" category="secondary"
size="small" size="small"
......
<script> <script>
import { GlButton, GlLink, GlCollapse, GlCard } from '@gitlab/ui'; import { GlBanner, GlButton, GlLink, GlCollapse } from '@gitlab/ui';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { s__ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { import {
MR_APPROVALS_PROMO_DISMISSED, MR_APPROVALS_PROMO_DISMISSED,
...@@ -12,11 +13,11 @@ const trackingMixin = Tracking.mixin({}); ...@@ -12,11 +13,11 @@ const trackingMixin = Tracking.mixin({});
export default { export default {
components: { components: {
GlBanner,
GlButton, GlButton,
GlLink, GlLink,
LocalStorageSync, LocalStorageSync,
GlCollapse, GlCollapse,
GlCard,
}, },
mixins: [trackingMixin], mixins: [trackingMixin],
inject: ['learnMorePath', 'promoImageAlt', 'promoImagePath', 'tryNowPath'], inject: ['learnMorePath', 'promoImageAlt', 'promoImagePath', 'tryNowPath'],
...@@ -24,20 +25,24 @@ export default { ...@@ -24,20 +25,24 @@ export default {
return { return {
// isReady - used to render components after local storage has synced // isReady - used to render components after local storage has synced
isReady: false, isReady: false,
// userManuallyCollapsed - set to true if the collapsible is collapsed
userManuallyCollapsed: false,
// isExpanded - the current collapsible state // isExpanded - the current collapsible state
isExpanded: true, isExpanded: true,
// isBannerVisible - is the banner visible
isBannerDismissed: false,
}; };
}, },
computed: { computed: {
icon() { icon() {
return this.isExpanded ? 'chevron-down' : 'chevron-right'; return this.isExpanded ? 'chevron-down' : 'chevron-right';
}, },
}, buttonAttributes() {
watch: { return {
userManuallyCollapsed(isCollapsed) { target: '_blank',
this.isExpanded = !isCollapsed; 'aria-label': s__('ApprovalRule|Learn more about merge request approval rules'),
'data-track-action': this.$options.trackingEvents.tryNowClick.action,
'data-track-label': this.$options.trackingEvents.tryNowClick.label,
'data-testid': 'promo-dismiss-btn',
};
}, },
}, },
mounted() { mounted() {
...@@ -50,8 +55,6 @@ export default { ...@@ -50,8 +55,6 @@ export default {
toggleCollapse() { toggleCollapse() {
// If we're expanded already, then the user tried to collapse... // If we're expanded already, then the user tried to collapse...
if (this.isExpanded) { if (this.isExpanded) {
this.userManuallyCollapsed = true;
const { action, ...options } = MR_APPROVALS_PROMO_TRACKING_EVENTS.collapsePromo; const { action, ...options } = MR_APPROVALS_PROMO_TRACKING_EVENTS.collapsePromo;
this.track(action, options); this.track(action, options);
} else { } else {
...@@ -61,6 +64,10 @@ export default { ...@@ -61,6 +64,10 @@ export default {
this.isExpanded = !this.isExpanded; this.isExpanded = !this.isExpanded;
}, },
hideBanner() {
this.isBannerDismissed = true;
this.isExpanded = false;
},
}, },
trackingEvents: MR_APPROVALS_PROMO_TRACKING_EVENTS, trackingEvents: MR_APPROVALS_PROMO_TRACKING_EVENTS,
i18n: MR_APPROVALS_PROMO_I18N, i18n: MR_APPROVALS_PROMO_I18N,
...@@ -71,55 +78,53 @@ export default { ...@@ -71,55 +78,53 @@ export default {
<template> <template>
<div class="gl-mt-2"> <div class="gl-mt-2">
<local-storage-sync <local-storage-sync
v-model="userManuallyCollapsed" v-model="isBannerDismissed"
:storage-key="$options.MR_APPROVALS_PROMO_DISMISSED" :storage-key="$options.MR_APPROVALS_PROMO_DISMISSED"
/> />
<template v-if="isReady"> <template v-if="isReady">
<p class="gl-mb-0 gl-text-gray-500"> <p class="gl-mb-0 gl-text-subtle">
{{ $options.i18n.summary }} {{ $options.i18n.summary }}
</p> </p>
<gl-button variant="link" :icon="icon" data-testid="collapse-btn" @click="toggleCollapse"> <gl-button
v-if="!isBannerDismissed"
variant="link"
:icon="icon"
data-testid="collapse-btn"
@click="toggleCollapse"
>
{{ $options.i18n.accordionTitle }} {{ $options.i18n.accordionTitle }}
</gl-button> </gl-button>
<gl-collapse v-model="isExpanded"> <gl-collapse v-if="!isBannerDismissed" v-model="isExpanded">
<gl-card class="gl-new-card" data-testid="mr-approval-rules"> <gl-banner
<div class="gl-flex gl-items-start gl-gap-6"> :title="$options.i18n.promoTitle"
<img :src="promoImagePath" :alt="promoImageAlt" class="svg" /> :svg-path="promoImagePath"
:button-text="$options.i18n.tryNow"
:button-link="tryNowPath"
:button-attributes="buttonAttributes"
class="gl-mt-3"
data-testid="mr-approval-rules"
@close="hideBanner"
>
<ul class="gl-mb-5 gl-list-inside gl-p-0">
<li v-for="(statement, index) in $options.i18n.valueStatements" :key="index">
{{ statement }}
</li>
</ul>
<div class="gl-grow"> <template #actions>
<h4 class="gl-mb-3 gl-mt-0 gl-text-base gl-leading-20"> <gl-link
{{ $options.i18n.promoTitle }} :href="learnMorePath"
</h4> target="_blank"
<ul class="gl-mb-3 gl-list-inside gl-p-0"> class="gl-ml-3"
<li v-for="(statement, index) in $options.i18n.valueStatements" :key="index"> :data-track-action="$options.trackingEvents.learnMoreClick.action"
{{ statement }} :data-track-label="$options.trackingEvents.learnMoreClick.label"
</li> >
</ul> {{ $options.i18n.learnMore }}
<div class="gl-flex gl-items-center gl-gap-4"> </gl-link>
<gl-button </template>
category="primary" </gl-banner>
variant="confirm"
:href="tryNowPath"
target="_blank"
:aria-label="s__('ApprovalRule|Learn more about merge request approval rules')"
:data-track-action="$options.trackingEvents.tryNowClick.action"
:data-track-label="$options.trackingEvents.tryNowClick.label"
>{{ $options.i18n.tryNow }}</gl-button
>
<gl-link
:href="learnMorePath"
target="_blank"
:data-track-action="$options.trackingEvents.learnMoreClick.action"
:data-track-label="$options.trackingEvents.learnMoreClick.label"
>
{{ $options.i18n.learnMore }}
</gl-link>
</div>
</div>
</div>
</gl-card>
</gl-collapse> </gl-collapse>
</template> </template>
</div> </div>
......
import { GlButton, GlLink, GlCollapse } from '@gitlab/ui'; import { GlBanner, GlButton, GlLink, GlCollapse } from '@gitlab/ui';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { shallowMountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper';
...@@ -36,6 +36,7 @@ describe('FreeTierPromo component', () => { ...@@ -36,6 +36,7 @@ describe('FreeTierPromo component', () => {
}, },
stubs: { stubs: {
LocalStorageSync, LocalStorageSync,
GlBanner,
}, },
}); });
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
...@@ -45,6 +46,7 @@ describe('FreeTierPromo component', () => { ...@@ -45,6 +46,7 @@ describe('FreeTierPromo component', () => {
const findCollapse = () => extendedWrapper(wrapper.findComponent(GlCollapse)); const findCollapse = () => extendedWrapper(wrapper.findComponent(GlCollapse));
const findLearnMore = () => findCollapse().findComponent(GlLink); const findLearnMore = () => findCollapse().findComponent(GlLink);
const findTryNow = () => findCollapse().findComponent(GlButton); const findTryNow = () => findCollapse().findComponent(GlButton);
const findBanner = () => wrapper.findComponent(GlBanner);
afterEach(() => { afterEach(() => {
unmockTracking(); unmockTracking();
...@@ -129,9 +131,7 @@ describe('FreeTierPromo component', () => { ...@@ -129,9 +131,7 @@ describe('FreeTierPromo component', () => {
}); });
it('shows the promo image', () => { it('shows the promo image', () => {
const promoImage = findCollapse().findByAltText('some promo image'); expect(findBanner().props('svgPath')).toBe('/some-image.svg');
expect(promoImage.element.src).toBe('/some-image.svg');
}); });
}); });
...@@ -148,14 +148,26 @@ describe('FreeTierPromo component', () => { ...@@ -148,14 +148,26 @@ describe('FreeTierPromo component', () => {
expect(findCollapse().props('visible')).toBe(false); expect(findCollapse().props('visible')).toBe(false);
}); });
it('updates local storage', () => {
expect(localStorage.setItem).toHaveBeenCalledWith(MR_APPROVALS_PROMO_DISMISSED, 'true');
});
it('updates button icon', () => { it('updates button icon', () => {
expect(findCollapseToggleButton().attributes('icon')).toBe(COLLAPSED_ICON); expect(findCollapseToggleButton().attributes('icon')).toBe(COLLAPSED_ICON);
}); });
}); });
describe('when user dismisses banner', () => {
beforeEach(() => {
findBanner().vm.$emit('close');
});
it('hides toggle, collapsible and banner', () => {
expect(findCollapseToggleButton().exists()).toBe(false);
expect(wrapper.findComponent(GlCollapse).exists()).toBe(false);
expect(findBanner().exists()).toBe(false);
});
it('updates local storage', () => {
expect(localStorage.setItem).toHaveBeenCalledWith(MR_APPROVALS_PROMO_DISMISSED, 'true');
});
});
}); });
describe('when local storage is initialized with mr_approvals_promo.dismissed=true', () => { describe('when local storage is initialized with mr_approvals_promo.dismissed=true', () => {
...@@ -166,30 +178,10 @@ describe('FreeTierPromo component', () => { ...@@ -166,30 +178,10 @@ describe('FreeTierPromo component', () => {
localStorage.setItem.mockClear(); localStorage.setItem.mockClear();
}); });
it('should show collapse container as collapsed', () => { it("doesn't render toggle, collapsible and banner", () => {
expect(findCollapse().props('visible')).toBe(false); expect(findCollapseToggleButton().exists()).toBe(false);
}); expect(wrapper.findComponent(GlCollapse).exists()).toBe(false);
expect(findBanner().exists()).toBe(false);
describe('when user clicks collapse toggle', () => {
beforeEach(() => {
findCollapseToggleButton().vm.$emit('click');
});
it('tracks intent to expand', () => {
expectTracking(undefined, trackingEvents.expandPromo);
});
it('expands the collapse component', () => {
expect(findCollapse().props('visible')).toBe(true);
});
it('does NOT update local storage', () => {
expect(localStorage.setItem).not.toHaveBeenCalled();
});
it('updates button icon', () => {
expect(findCollapseToggleButton().attributes('icon')).toBe(EXPANDED_ICON);
});
}); });
}); });
}); });
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册