Add support for deferred links in persistent user callouts
Persistent user callouts now support deferred links, which are links that can be used to dismiss the callout, and then proceed to follow the link's original location. This ensures that the callout dismissal is properly recorded before the user leaves the page.
This commit is contained in:
parent
f74387d298
commit
4f12a4dde1
|
@ -1,13 +1,17 @@
|
|||
import { parseBoolean } from './lib/utils/common_utils';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { __ } from './locale';
|
||||
import Flash from './flash';
|
||||
|
||||
const DEFERRED_LINK_CLASS = 'deferred-link';
|
||||
|
||||
export default class PersistentUserCallout {
|
||||
constructor(container) {
|
||||
const { dismissEndpoint, featureId } = container.dataset;
|
||||
const { dismissEndpoint, featureId, deferLinks } = container.dataset;
|
||||
this.container = container;
|
||||
this.dismissEndpoint = dismissEndpoint;
|
||||
this.featureId = featureId;
|
||||
this.deferLinks = parseBoolean(deferLinks);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
@ -15,9 +19,21 @@ export default class PersistentUserCallout {
|
|||
init() {
|
||||
const closeButton = this.container.querySelector('.js-close');
|
||||
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();
|
||||
|
||||
axios
|
||||
|
@ -26,6 +42,11 @@ export default class PersistentUserCallout {
|
|||
})
|
||||
.then(() => {
|
||||
this.container.remove();
|
||||
|
||||
if (deferredLinkOptions) {
|
||||
const { href, target } = deferredLinkOptions;
|
||||
window.open(href, target);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import PersistentUserCallout from '~/persistent_user_callout';
|
||||
|
||||
function initPrivacyPolicyUpdateCallout() {
|
||||
const callout = document.querySelector('.privacy-policy-update-64341');
|
||||
PersistentUserCallout.factory(callout);
|
||||
}
|
||||
|
||||
export default initPrivacyPolicyUpdateCallout;
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add support for deferred links in persistent user callouts.
|
||||
merge_request: 30818
|
||||
author:
|
||||
type: added
|
|
@ -22,6 +22,24 @@ describe('PersistentUserCallout', () => {
|
|||
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', () => {
|
||||
let button;
|
||||
let mockAxios;
|
||||
|
@ -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', () => {
|
||||
it('returns an instance of PersistentUserCallout with the provided container property', () => {
|
||||
const fixture = createFixture();
|
||||
|
|
Loading…
Reference in New Issue