Commit 8cb7781b authored by Savas Vedova's avatar Savas Vedova

Merge branch '227259-feature-highlight-popover' into 'master'

Implement feature highlight popover in Vue

See merge request gitlab-org/gitlab!53648
parents df39fe86 463b9c04
export const POPOVER_TARGET_ID = 'feature-highlight-trigger';
<script>
import {
GlPopover,
GlSprintf,
GlLink,
GlButton,
GlSafeHtmlDirective as SafeHtml,
} from '@gitlab/ui';
import clusterPopover from '@gitlab/svgs/dist/illustrations/cluster_popover.svg';
import { __ } from '~/locale';
import { dismiss } from './feature_highlight_helper';
import { POPOVER_TARGET_ID } from './constants';
export default {
components: {
GlPopover,
GlSprintf,
GlLink,
GlButton,
},
directives: {
SafeHtml,
},
props: {
autoDevopsHelpPath: {
type: String,
required: true,
},
highlightId: {
type: String,
required: true,
},
dismissEndpoint: {
type: String,
required: true,
},
},
data() {
return {
dismissed: false,
triggerHidden: false,
};
},
methods: {
dismiss() {
dismiss(this.dismissEndpoint, this.highlightId);
this.$refs.popover.$emit('close');
this.dismissed = true;
},
hideTrigger() {
if (this.dismissed) {
this.triggerHidden = true;
}
},
},
clusterPopover,
targetId: POPOVER_TARGET_ID,
i18n: {
highlightMessage: __('Allows you to add and manage Kubernetes clusters.'),
autoDevopsProTipMessage: __(
'Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!',
),
dismissButtonLabel: __('Got it!'),
},
};
</script>
<template>
<div class="gl-ml-3">
<span v-if="!triggerHidden" :id="$options.targetId" class="feature-highlight"></span>
<gl-popover
ref="popover"
:target="$options.targetId"
:css-classes="['feature-highlight-popover']"
triggers="hover"
container="body"
placement="right"
boundary="viewport"
@hidden="hideTrigger"
>
<span
v-safe-html="$options.clusterPopover"
class="feature-highlight-illustration gl-display-flex gl-justify-content-center gl-py-4 gl-w-full"
></span>
<div class="gl-px-4 gl-py-5">
<p>
{{ $options.i18n.highlightMessage }}
</p>
<p>
<gl-sprintf :message="$options.i18n.autoDevopsProTipMessage">
<template #link="{ content }">
<gl-link class="gl-font-sm" :href="autoDevopsHelpPath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
<gl-button size="small" icon="thumb-up" variant="confirm" @click="dismiss">
{{ $options.i18n.dismissButtonLabel }}
</gl-button>
</div>
</gl-popover>
</div>
</template>
import Vue from 'vue';
const init = async () => {
const el = document.querySelector('.js-feature-highlight');
if (!el) {
return null;
}
const { autoDevopsHelpPath, highlight: highlightId, dismissEndpoint } = el.dataset;
const { default: FeatureHighlight } = await import(
/* webpackChunkName: 'feature_highlight' */ './feature_highlight_popover.vue'
);
return new Vue({
el,
render: (h) =>
h(FeatureHighlight, {
props: {
autoDevopsHelpPath,
highlightId,
dismissEndpoint,
},
}),
});
};
export default init;
...@@ -23934,6 +23934,9 @@ msgstr "" ...@@ -23934,6 +23934,9 @@ msgstr ""
msgid "Protip:" msgid "Protip:"
msgstr "" msgstr ""
msgid "Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!"
msgstr ""
msgid "Protocol" msgid "Protocol"
msgstr "" msgstr ""
......
...@@ -38,7 +38,9 @@ jest.mock('@gitlab/ui/dist/components/base/popover/popover.js', () => ({ ...@@ -38,7 +38,9 @@ jest.mock('@gitlab/ui/dist/components/base/popover/popover.js', () => ({
required: false, required: false,
default: () => [], default: () => [],
}, },
...Object.fromEntries(['target', 'triggers', 'placement'].map((prop) => [prop, {}])), ...Object.fromEntries(
['target', 'triggers', 'placement', 'boundary', 'container'].map((prop) => [prop, {}]),
),
}, },
render(h) { render(h) {
return h( return h(
......
import { mount } from '@vue/test-utils';
import { GlPopover, GlLink, GlButton } from '@gitlab/ui';
import FeatureHighlightPopover from '~/feature_highlight/feature_highlight_popover.vue';
import { dismiss } from '~/feature_highlight/feature_highlight_helper';
import { POPOVER_TARGET_ID } from '~/feature_highlight/constants';
jest.mock('~/feature_highlight/feature_highlight_helper');
describe('feature_highlight/feature_highlight_popover', () => {
let wrapper;
const props = {
autoDevopsHelpPath: '/help/autodevops',
highlightId: '123',
dismissEndpoint: '/api/dismiss',
};
const buildWrapper = (propsData = props) => {
wrapper = mount(FeatureHighlightPopover, {
propsData,
});
};
const findPopoverTarget = () => wrapper.find(`#${POPOVER_TARGET_ID}`);
const findPopover = () => wrapper.findComponent(GlPopover);
const findAutoDevopsHelpLink = () => wrapper.findComponent(GlLink);
const findDismissButton = () => wrapper.findComponent(GlButton);
beforeEach(() => {
buildWrapper();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders popover target', () => {
expect(findPopoverTarget().exists()).toBe(true);
});
it('renders popover', () => {
expect(findPopover().props()).toMatchObject({
target: POPOVER_TARGET_ID,
cssClasses: ['feature-highlight-popover'],
triggers: 'hover',
container: 'body',
placement: 'right',
boundary: 'viewport',
});
});
it('renders link that points to the autodevops help page', () => {
expect(findAutoDevopsHelpLink().attributes().href).toBe(props.autoDevopsHelpPath);
expect(findAutoDevopsHelpLink().text()).toBe('Auto DevOps');
});
it('renders dismiss button', () => {
expect(findDismissButton().props()).toMatchObject({
size: 'small',
icon: 'thumb-up',
variant: 'confirm',
});
});
it('dismisses popover when dismiss button is clicked', async () => {
await findDismissButton().trigger('click');
expect(findPopover().emitted('close')).toHaveLength(1);
expect(dismiss).toHaveBeenCalledWith(props.dismissEndpoint, props.highlightId);
});
describe('when popover is dismissed and hidden', () => {
it('hides the popover target', async () => {
await findDismissButton().trigger('click');
findPopover().vm.$emit('hidden');
await wrapper.vm.$nextTick();
expect(findPopoverTarget().exists()).toBe(false);
});
});
});
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