diff --git a/assets/js/components/PostMessages.js b/assets/js/components/PostMessages.js new file mode 100644 index 00000000..9fda0fe3 --- /dev/null +++ b/assets/js/components/PostMessages.js @@ -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; +}); diff --git a/spec/js/fixtures/PostMessages.html b/spec/js/fixtures/PostMessages.html new file mode 100644 index 00000000..09b877c7 --- /dev/null +++ b/spec/js/fixtures/PostMessages.html @@ -0,0 +1,54 @@ +
+
+ + +
+

The first content heading

+

+ The first piece of dummy content with a link to an + external site + within it. +

+ +

The second content heading

+

+ The second piece of dummy content with a link to some + internal content + within it. +

+ +

The third content heading

+

The third piece of dummy content with no links.

+
+
+ + +
diff --git a/spec/js/test-main.js b/spec/js/test-main.js index d782d81e..54ea521b 100644 --- a/spec/js/test-main.js +++ b/spec/js/test-main.js @@ -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', diff --git a/spec/js/tests/PostMessages_spec.js b/spec/js/tests/PostMessages_spec.js new file mode 100644 index 00000000..b8748c97 --- /dev/null +++ b/spec/js/tests/PostMessages_spec.js @@ -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); + }) + }); +});