From 70bb2e9d07204d0f4746a25a3227fb93895940ba Mon Sep 17 00:00:00 2001 From: Keegan Date: Thu, 26 May 2016 18:45:22 -0700 Subject: [PATCH 1/4] Add destroy function --- dev/config-2.js | 38 +++++++++++++++++++++ dev/custom-fn.js | 10 ++++++ dev/dev.js | 5 ++- src/scripts/lib/clay-config.js | 60 ++++++++++++++++++++++++++++++---- test/spec/lib/clay-config.js | 44 +++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 dev/config-2.js diff --git a/dev/config-2.js b/dev/config-2.js new file mode 100644 index 0000000..37d60e4 --- /dev/null +++ b/dev/config-2.js @@ -0,0 +1,38 @@ +'use strict'; +/* eslint-disable quotes */ + +module.exports = [ + { + "type": "heading", + "id": "main-heading", + "defaultValue": "Clay Test Page", + "size": 1 + }, + { + "type": "text", + "defaultValue": "This is config 2" + }, + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "This is a section" + }, + { + "type": "input", + "appKey": "email", + "defaultValue": "", + "label": "Input Field", + "description": "This is a new field", + "attributes": { + "placeholder": "Placeholder set with attributes" + } + } + ] + }, + { + "type": "submit", + "defaultValue": "Save" + } +]; diff --git a/dev/custom-fn.js b/dev/custom-fn.js index fe004aa..d6202a4 100644 --- a/dev/custom-fn.js +++ b/dev/custom-fn.js @@ -16,10 +16,20 @@ module.exports = function() { } } + /** + * @returns {void} + */ + function handleButtonClick() { + Clay.config = Clay.meta.userData.config2; + Clay.build(); + } + Clay.on(Clay.EVENTS.AFTER_BUILD, function() { var coolStuffToggle = Clay.getItemByAppKey('cool_stuff'); toggleBackground.call(coolStuffToggle); coolStuffToggle.on('change', toggleBackground); + + Clay.getItemById('testButton').on('click', handleButtonClick); }); console.log('userData: ', Clay.meta.userData); diff --git a/dev/dev.js b/dev/dev.js index 72cccee..502edd2 100644 --- a/dev/dev.js +++ b/dev/dev.js @@ -6,7 +6,10 @@ window.claySettings = {}; window.customFn = require('./custom-fn.js'); window.clayComponents = require('../src/scripts/components'); window.clayMeta = require('../test/fixture').meta({ - userData: {foo: 'bar'} + userData: { + foo: 'bar', + config2: require('./config-2') + } }); var platform = window.navigator.userAgent.match(/Android/) ? 'android' : 'ios'; diff --git a/src/scripts/lib/clay-config.js b/src/scripts/lib/clay-config.js index e809876..4546f6f 100644 --- a/src/scripts/lib/clay-config.js +++ b/src/scripts/lib/clay-config.js @@ -33,10 +33,22 @@ function ClayConfig(settings, config, $rootContainer, meta) { var self = this; var _settings = _.copyObj(settings); - var _items = []; - var _itemsById = {}; - var _itemsByAppKey = {}; - var _isBuilt = false; + var _items; + var _itemsById; + var _itemsByAppKey; + var _isBuilt; + + /** + * Initialize the item arrays and objects + * @private + * @return {void} + */ + function _initializeItems() { + _items = []; + _itemsById = {}; + _itemsByAppKey = {}; + _isBuilt = false; + } /** * Add item(s) to the config @@ -97,6 +109,7 @@ function ClayConfig(settings, config, $rootContainer, meta) { } self.meta = meta; + self.$rootContainer = $rootContainer; self.EVENTS = { /** @@ -111,7 +124,21 @@ function ClayConfig(settings, config, $rootContainer, meta) { * value set * @const */ - AFTER_BUILD: 'AFTER_BUILD' + AFTER_BUILD: 'AFTER_BUILD', + + /** + * Called if .build() is executed after the page has already been built and + * before the existing content is destroyed + * @const + */ + BEFORE_DESTROY: 'BEFORE_DESTROY', + + /** + * Called if .build() is executed after the page has already been built and after + * the existing content is destroyed + * @const + */ + AFTER_DESTROY: 'AFTER_DESTROY' }; utils.updateProperties(self.EVENTS, {writable: false}); @@ -172,18 +199,39 @@ function ClayConfig(settings, config, $rootContainer, meta) { // @todo maybe don't do this and force the static method self.registerComponent = ClayConfig.registerComponent; + /** + * Empties the root container + * @return {void} + */ + self.destroy = function() { + var el = $rootContainer[0]; + self.trigger(self.EVENTS.BEFORE_DESTROY); + while (el.firstChild) { + el.removeChild(el.firstChild); + } + _initializeItems(); + self.trigger(self.EVENTS.AFTER_DESTROY); + }; + /** * Build the config page. This must be run before any of the get methods can be run + * If you call this method after the page has already been built, teh page will be + * destroyed and built again. * @returns {ClayConfig} */ self.build = function() { + if (_isBuilt) { + self.destroy(); + } self.trigger(self.EVENTS.BEFORE_BUILD); - _addItems(config, $rootContainer); + _addItems(self.config, $rootContainer); _isBuilt = true; self.trigger(self.EVENTS.AFTER_BUILD); return self; }; + _initializeItems(); + // attach event methods ClayEvents.call(self, $rootContainer); diff --git a/test/spec/lib/clay-config.js b/test/spec/lib/clay-config.js index dba8b34..69537b9 100644 --- a/test/spec/lib/clay-config.js +++ b/test/spec/lib/clay-config.js @@ -254,6 +254,50 @@ describe('ClayConfig', function() { }); }); + describe('.destroy().', function() { + it('Destroys all the items on the page and in the items array', function() { + var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false); + + clayConfig.build(); + clayConfig.destroy(); + + // this should throw because the config has not been built yet + assert.throws(clayConfig.getAllItems); + + // there should be no DOM inside the container + assert.strictEqual(clayConfig.$rootContainer[0].children.length, 0); + }); + + it('dispatches the BEFORE_DESTROY event at the right time', function(done) { + var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false); + + clayConfig.on(clayConfig.EVENTS.BEFORE_DESTROY, function() { + assert.strictEqual(clayConfig.getAllItems().length, 3); + done(); + }); + + clayConfig.build(); + clayConfig.destroy(); + + assert.strictEqual(clayConfig.getAllItems().length, 0); + }); + + it('dispatches the AFTER_DESTROY event at the right time', function(done) { + var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false); + + clayConfig.on(clayConfig.EVENTS.AFTER_DESTROY, function() { + + // this should throw because the config has not been built yet + assert.throws(clayConfig.getAllItems); + done(); + }); + + clayConfig.build(); + assert.strictEqual(clayConfig.getAllItems().length, 3); + clayConfig.destroy(); + }); + }); + describe('.build()', function() { it('dispatches the BEFORE_BUILD event at the right time', function(done) { var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false); From 6f3873ad6b8cf7ed345bc93e15c17558862f9a04 Mon Sep 17 00:00:00 2001 From: Keegan Date: Sat, 28 May 2016 13:00:55 -0700 Subject: [PATCH 2/4] add tests for new clayConfig.build() behavior --- test/spec/lib/clay-config.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/spec/lib/clay-config.js b/test/spec/lib/clay-config.js index 69537b9..80d57a9 100644 --- a/test/spec/lib/clay-config.js +++ b/test/spec/lib/clay-config.js @@ -299,6 +299,24 @@ describe('ClayConfig', function() { }); describe('.build()', function() { + it('Destroys the config if called a consecutive time', function() { + var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false); + var destroyHandlerSpy = sinon.spy(); + + clayConfig.on(clayConfig.EVENTS.AFTER_DESTROY, destroyHandlerSpy); + + clayConfig.build(); + assert.strictEqual(clayConfig.getAllItems().length, 3); + + clayConfig.config = fixtures.config(['select']); + clayConfig.build(); + assert.strictEqual(clayConfig.getAllItems().length, 1); + assert(destroyHandlerSpy.calledOnce); + + clayConfig.build(); + assert(destroyHandlerSpy.calledTwice); + }); + it('dispatches the BEFORE_BUILD event at the right time', function(done) { var clayConfig = fixtures.clayConfig(['input', 'text', 'input'], false); clayConfig.on(clayConfig.EVENTS.BEFORE_BUILD, function() { From dc33aa0b5de40637669762e1b26f37c266cc053c Mon Sep 17 00:00:00 2001 From: Keegan Date: Sat, 28 May 2016 13:08:45 -0700 Subject: [PATCH 3/4] update docs --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f69991..b0926d4 100755 --- a/README.md +++ b/README.md @@ -854,6 +854,8 @@ This is the main way of talking to your generated config page. An instance of th |----------|------|-------------| | `.EVENTS.BEFORE_BUILD` | String | Dispatched prior to building the page. | | `.EVENTS.AFTER_BUILD` | String | Dispatched after building the page. | +| `.EVENTS.BEFORE_DESTROY` | String | Dispatched prior to destroying the page. | +| `.EVENTS.AFTER_DESTROY` | String | Dispatched after destroying the page. | | `.config` | Array | Reference to the config passed to the constructor and used for generating the page. | | `.meta` | Object | Contains information about the current user and watch | | `.meta.activeWatchInfo` | watchinfo\|null | An object containing information on the currently connected Pebble smartwatch or null if unavailable. Read more [here](https://developer.pebble.com/docs/js/Pebble/#getActiveWatchInfo). | @@ -871,7 +873,8 @@ This is the main way of talking to your generated config page. An instance of th | `.getItemById( [string] id )` | `ConfigItem\|undefined` - a single `ConfigItem` that has the provided `id`, otherwise `undefined`. | | `.getItemsByType( [string] type )` | `Array.` - an array of config items that match the provided `type`. | | `.serialize()` | `Object` - an object representing all items with an `appKey` where the key is the `appKey` and the value is an object with the `value` property set to the result of running `.get()` on the Clay item. If the Clay item has a `precision` property set, it is included in the object | -| `.build()`
Builds the config page. Will dispatch the `BEFORE_BUILD` event prior to building the page, then the `AFTER_BUILD` event once it is complete. | `ClayConfig` | +| `.build()`
Builds the config page. Will dispatch the `BEFORE_BUILD` event prior to building the page, then the `AFTER_BUILD` event once it is complete. If the config page has already been built, then the `ClayConfig.destroy()` method will be executed prior to building the page again. | `ClayConfig` | +| `.destroy()`
Destroys the config page. Will dispatch the `BEFORE_DESTROY` event prior to destroying the page, then the `AFTER_DESTROY` event once it is complete. This method wipes the config page completely, including all existing items. You will need to make sure that you re-attach your event handlers for any items that are replaced | `ClayConfig` | | `.on( [string] events, [function] handler )`
Register an event to the provided handler. The handler will be called with this instance of `ClayConfig` as the context. If you wish to register multiple events to the same handler, then separate the events with a space | `ClayConfig` | | `.off( [function] handler )`
Remove the given event handler. **NOTE:** This will remove the handler from all registered events. | `ClayConfig` | | `.trigger( [string] name, [object] eventObj={} )`
Trigger the provided event and optionally pass extra data to the handler. | `ClayConfig` | From 12c420d861927caa4915d12aaae8c013f5223062 Mon Sep 17 00:00:00 2001 From: Keegan Date: Sat, 28 May 2016 13:11:53 -0700 Subject: [PATCH 4/4] make ClayConfig.destroy() return self --- src/scripts/lib/clay-config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scripts/lib/clay-config.js b/src/scripts/lib/clay-config.js index 4546f6f..b2ab0e0 100644 --- a/src/scripts/lib/clay-config.js +++ b/src/scripts/lib/clay-config.js @@ -201,7 +201,7 @@ function ClayConfig(settings, config, $rootContainer, meta) { /** * Empties the root container - * @return {void} + * @returns {ClayConfig} */ self.destroy = function() { var el = $rootContainer[0]; @@ -211,6 +211,7 @@ function ClayConfig(settings, config, $rootContainer, meta) { } _initializeItems(); self.trigger(self.EVENTS.AFTER_DESTROY); + return self; }; /**