Commit 31d13a41 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '64341-user-callout-deferred-link-support' into 'master'

Add support for deferred links in persistent user callouts.

Closes #64341

See merge request gitlab-org/gitlab-ce!30818
parents f74387d2 4f12a4dd
import { parseBoolean } from './lib/utils/common_utils';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { __ } from './locale'; import { __ } from './locale';
import Flash from './flash'; import Flash from './flash';
const DEFERRED_LINK_CLASS = 'deferred-link';
export default class PersistentUserCallout { export default class PersistentUserCallout {
constructor(container) { constructor(container) {
const { dismissEndpoint, featureId } = container.dataset; const { dismissEndpoint, featureId, deferLinks } = container.dataset;
this.container = container; this.container = container;
this.dismissEndpoint = dismissEndpoint; this.dismissEndpoint = dismissEndpoint;
this.featureId = featureId; this.featureId = featureId;
this.deferLinks = parseBoolean(deferLinks);
this.init(); this.init();
} }
...@@ -15,9 +19,21 @@ export default class PersistentUserCallout { ...@@ -15,9 +19,21 @@ export default class PersistentUserCallout {
init() { init() {
const closeButton = this.container.querySelector('.js-close'); const closeButton = this.container.querySelector('.js-close');
closeButton.addEventListener('click', event => this.dismiss(event)); closeButton.addEventListener('click', event => this.dismiss(event));
if (this.deferLinks) {
this.container.addEventListener('click', event => {
const isDeferredLink = event.target.classList.contains(DEFERRED_LINK_CLASS);
if (isDeferredLink) {
const { href, target } = event.target;
this.dismiss(event, { href, target });
}
});
}
} }
dismiss(event) { dismiss(event, deferredLinkOptions = null) {
event.preventDefault(); event.preventDefault();
axios axios
...@@ -26,6 +42,11 @@ export default class PersistentUserCallout { ...@@ -26,6 +42,11 @@ export default class PersistentUserCallout {
}) })
.then(() => { .then(() => {
this.container.remove(); this.container.remove();
if (deferredLinkOptions) {
const { href, target } = deferredLinkOptions;
window.open(href, target);
}
}) })
.catch(() => { .catch(() => {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.')); Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
......
import PersistentUserCallout from '~/persistent_user_callout';
function initPrivacyPolicyUpdateCallout() {
const callout = document.querySelector('.privacy-policy-update-64341');
PersistentUserCallout.factory(callout);
}
export default initPrivacyPolicyUpdateCallout;
---
title: Add support for deferred links in persistent user callouts.
merge_request: 30818
author:
type: added
...@@ -22,6 +22,24 @@ describe('PersistentUserCallout', () => { ...@@ -22,6 +22,24 @@ describe('PersistentUserCallout', () => {
return fixture; return fixture;
} }
function createDeferredLinkFixture() {
const fixture = document.createElement('div');
fixture.innerHTML = `
<div
class="container"
data-dismiss-endpoint="${dismissEndpoint}"
data-feature-id="${featureName}"
data-defer-links="true"
>
<button type="button" class="js-close"></button>
<a href="/somewhere-pleasant" target="_blank" class="deferred-link">A link</a>
<a href="/somewhere-else" target="_blank" class="normal-link">Another link</a>
</div>
`;
return fixture;
}
describe('dismiss', () => { describe('dismiss', () => {
let button; let button;
let mockAxios; let mockAxios;
...@@ -74,6 +92,75 @@ describe('PersistentUserCallout', () => { ...@@ -74,6 +92,75 @@ describe('PersistentUserCallout', () => {
}); });
}); });
describe('deferred links', () => {
let button;
let deferredLink;
let normalLink;
let mockAxios;
let persistentUserCallout;
let windowSpy;
beforeEach(() => {
const fixture = createDeferredLinkFixture();
const container = fixture.querySelector('.container');
button = fixture.querySelector('.js-close');
deferredLink = fixture.querySelector('.deferred-link');
normalLink = fixture.querySelector('.normal-link');
mockAxios = new MockAdapter(axios);
persistentUserCallout = new PersistentUserCallout(container);
spyOn(persistentUserCallout.container, 'remove');
windowSpy = spyOn(window, 'open').and.callFake(() => {});
});
afterEach(() => {
mockAxios.restore();
});
it('defers loading of a link until callout is dismissed', done => {
const { href, target } = deferredLink;
mockAxios.onPost(dismissEndpoint).replyOnce(200);
deferredLink.click();
setTimeoutPromise()
.then(() => {
expect(windowSpy).toHaveBeenCalledWith(href, target);
expect(persistentUserCallout.container.remove).toHaveBeenCalled();
expect(mockAxios.history.post[0].data).toBe(
JSON.stringify({ feature_name: featureName }),
);
})
.then(done)
.catch(done.fail);
});
it('does not dismiss callout on non-deferred links', done => {
normalLink.click();
setTimeoutPromise()
.then(() => {
expect(windowSpy).not.toHaveBeenCalled();
expect(persistentUserCallout.container.remove).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
it('does not follow link when notification is closed', done => {
mockAxios.onPost(dismissEndpoint).replyOnce(200);
button.click();
setTimeoutPromise()
.then(() => {
expect(windowSpy).not.toHaveBeenCalled();
expect(persistentUserCallout.container.remove).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
});
describe('factory', () => { describe('factory', () => {
it('returns an instance of PersistentUserCallout with the provided container property', () => { it('returns an instance of PersistentUserCallout with the provided container property', () => {
const fixture = createFixture(); const fixture = createFixture();
......
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