Skip to content

Commit

Permalink
Adds PostMessages component
Browse files Browse the repository at this point in the history
- Designed to communicate between iFrame and parent on MoneyHelper
  • Loading branch information
davidtrussler authored and benlovell committed Mar 5, 2024
1 parent ec2d70d commit e3ce7d1
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 0 deletions.
85 changes: 85 additions & 0 deletions assets/js/components/PostMessages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* This is a generic component designed to allow communication between
* MAS components and a host page on the Money Helper site
* It uses the postMessage method to send an object to the host environment
*/
define(['DoughBaseComponent'],
function(DoughBaseComponent) {
'use strict';

var PostMessages,
defaultConfig = {},
message = {
jumpLink: {
id: '',
offset: 0
}
};

PostMessages = function($el, config) {
PostMessages.baseConstructor.call(this, $el, config, defaultConfig);

this.message = message;
};

/**
* Inherit from base module, for shared methods and interface
*/
DoughBaseComponent.extend(PostMessages);

PostMessages.componentName = 'PostMessages';

/**
* Adds listeners for click events to jump links
*/
PostMessages.prototype._addEvents = function() {
var _this = this;
var anchors = this.$el.find('a');

for (var anchor in anchors) {
if (anchors[anchor].href && anchors[anchor].href.indexOf('#') > -1) {
$(anchors[anchor]).on('click', function(e) {
e.preventDefault();
_this._updateMessage(e.target.href.split('#')[1]);
})
}
};
}

/**
* Updates the message with vertical offset value for the supplied element
*/
PostMessages.prototype._updateMessage = function(id) {
var offset = this._getOffset(id);
this.message.jumpLink.id = id;
this.message.jumpLink.offset = offset;

this._sendMessage();
}

/**
* Gets the vertical offset value of the required element
*/
PostMessages.prototype._getOffset = function(id) {
var el = this.$el.find('#' + id);

return el[0].getBoundingClientRect().top;
}

/**
* Sends the message
*/
PostMessages.prototype._sendMessage = function() {
window.parent.postMessage(this.message, '*');
}

/**
* @param {Promise} initialised
*/
PostMessages.prototype.init = function(initialised) {
this._initialisedSuccess(initialised);
this._addEvents();
};

return PostMessages;
});
54 changes: 54 additions & 0 deletions spec/js/fixtures/PostMessages.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div>
<div data-dough-component="PostMessages">
<section id="jump-links">
<a id="jump_link_1" href="#content_1">Link to the first bit of content</a>
<a id="jump_link_2" href="#content_2">Link to the second bit of content</a>
<a id="jump_link_3" href="#content_3">Link to the third bit of content</a>
</section>

<section id="content">
<h2 id="content_1">The first content heading</h2>
<p>
The first piece of dummy content with a link to an
<a id="external_link" href="https://www.external-site.com">external site</a>
within it.
</p>

<h2 id="content_2">The second content heading</h2>
<p>
The second piece of dummy content with a link to some
<a id="internal_link" href="/en/tools/debt-advice-locator/bristol">internal content</a>
within it.
</p>

<h2 id="content_3">The third content heading</h2>
<p>The third piece of dummy content with no links.</p>
</section>
</div>

<style type="text/css">
body * {
margin: 0;
padding: 0;
}

#content_1,
#content_2,
#content_3 {
position: absolute;
top: 150px;
}

#content_1 {
top: 150px;
}

#content_2 {
top: 300px;
}

#content_3 {
top: 450px;
}
</style>
</div>
1 change: 1 addition & 0 deletions spec/js/test-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require.config({
CovidBanner: 'assets/js/components/CovidBanner',
ConfirmableForm: 'assets/js/components/ConfirmableForm',
PopupTip: 'assets/js/components/PopupTip',
PostMessages: 'assets/js/components/PostMessages',
Print: 'assets/js/components/Print',
TabSelector: 'assets/js/components/TabSelector',
RangeInput: 'assets/js/components/RangeInput',
Expand Down
120 changes: 120 additions & 0 deletions spec/js/tests/PostMessages_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
describe('PostMessages component', function() {
'use strict';

beforeEach(function(done) {
var self = this;

fixture.setBase('spec/js/fixtures');

requirejs(
['PostMessages'],
function(PostMessages) {
fixture.load('PostMessages.html');

self.component = $(fixture.el).find('[data-dough-component="PostMessages"]');
self.postMessages = new PostMessages(self.component);
self.message = self.postMessages.message;

done();
}, done);
});

afterEach(function() {
fixture.cleanup();
});

describe('On initialising', function() {
it('Calls the _addEvents method', function() {
var addEventsSpy = sinon.spy(this.postMessages, '_addEvents');

this.postMessages.init();
expect(addEventsSpy.calledOnce).to.be.true;

addEventsSpy.restore();
});
});

describe('On clicking a jump link', function() {
it('Calls the updateMessage method with the correct argument', function() {
var updateMessageSpy = sinon.spy(this.postMessages, '_updateMessage');

this.postMessages._addEvents();

this.component.find('#jump_link_1').trigger('click');
expect(updateMessageSpy.callCount).to.equal(1);
assert(updateMessageSpy.calledWith('content_1'));

this.component.find('#jump_link_2').trigger('click');
expect(updateMessageSpy.callCount).to.equal(2);
assert(updateMessageSpy.calledWith('content_2'));

this.component.find('#jump_link_3').trigger('click');
expect(updateMessageSpy.callCount).to.equal(3);
assert(updateMessageSpy.calledWith('content_3'));

this.component.find('#external_link').trigger('click');
expect(updateMessageSpy.callCount).to.equal(3);

this.component.find('#internal_link').trigger('click');
expect(updateMessageSpy.callCount).to.equal(3);

updateMessageSpy.restore();
})
});

describe('On calling the updateMessage method', function() {
it('Calls the getOffset method with the correct argument', function() {
var getOffsetSpy = sinon.spy(this.postMessages, '_getOffset');

this.postMessages._updateMessage('content_1');
expect(getOffsetSpy.callCount).to.equal(1);
assert(getOffsetSpy.calledWith('content_1'));

this.postMessages._updateMessage('content_2');
expect(getOffsetSpy.callCount).to.equal(2);
assert(getOffsetSpy.calledWith('content_2'));

this.postMessages._updateMessage('content_3');
expect(getOffsetSpy.callCount).to.equal(3);
assert(getOffsetSpy.calledWith('content_3'));

getOffsetSpy.restore();
});

it('Updates the message with the correct values', function() {
var getOffsetStub = sinon.stub(this.postMessages, '_getOffset');

getOffsetStub.returns(120);
this.postMessages._updateMessage('content_1');

expect(this.message.jumpLink.id).to.equal('content_1');
expect(this.message.jumpLink.offset).to.equal(120);

getOffsetStub.restore();
});

it('Calls the sendMessage method', function() {
var sendMessageSpy = sinon.spy(this.postMessages, '_sendMessage');

this.postMessages._updateMessage('content_1');
expect(sendMessageSpy.calledOnce).to.be.true;

sendMessageSpy.restore();
});
});

describe('On calling the getOffset method', function() {
it('Returns the correct value', function() {
var offset;

offset = this.postMessages._getOffset('content_1');
expect(offset).to.equal(150);

offset = this.postMessages._getOffset('content_2');
expect(offset).to.equal(300);

offset = this.postMessages._getOffset('content_3');
expect(offset).to.equal(450);
})
});
});

0 comments on commit e3ce7d1

Please sign in to comment.