From 4fa0efbfc7d5ab68523e12b030399692a1d7862c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20K=C3=A1ntor?= Date: Fri, 9 Mar 2018 12:11:37 +0100 Subject: [PATCH] fix(components/Root): RootComponent now unsubscribes deleted elements fixes #78 --- src/components/root/Root.js | 2 ++ src/components/root/Root.test.js | 20 ++++++++++++++++++-- src/manual_test.js | 3 +++ src/state_handler.js | 3 +++ src/state_handler.test.js | 14 ++++++++++++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/components/root/Root.js b/src/components/root/Root.js index e9e2637f..559a0b32 100644 --- a/src/components/root/Root.js +++ b/src/components/root/Root.js @@ -33,6 +33,8 @@ const RootComponent = Component({ 'render': (args, selection, data) => { const body = selection.select('body') body.selectAll('*').remove() + // "unsubscribe" the elements we've just removed + if (args.state_handler) args.state_handler.reset() if (data instanceof Array) data.map((definition) => { const updated_definition = Object.assign({'args': {}}, definition) updated_definition.args.state_handler = args.state_handler diff --git a/src/components/root/Root.test.js b/src/components/root/Root.test.js index b0bc4f4f..bd394733 100644 --- a/src/components/root/Root.test.js +++ b/src/components/root/Root.test.js @@ -116,7 +116,7 @@ describe('Root component', function() { }) it('variables are initialized in state', function() { - const state_handler = {'init_variable': sinon.spy()} + const state_handler = {'init_variable': sinon.spy(), 'reset': sinon.spy()} const my_default = sinon.spy() call_render_with({ 'parser': (component) => () => component.args.state_handler.init_variable( @@ -131,8 +131,24 @@ describe('Root component', function() { state_handler.init_variable.should.be.calledWith('foo', my_default) }) + it('calls state_handler\'s reset', function() { + const state_handler = {'reset': sinon.spy()} + const my_default = sinon.spy() + call_render_with({ + 'parser': (component) => () => component.args.state_handler.init_variable( + component.args.variable, component.args.default), + 'component_args': {'title': 'x', 'state_handler': state_handler}, + 'render_args': [ + { 'component': 'dropdown', 'args': { + 'default': my_default, 'variable': 'foo', + }, 'data': [] } + ] + }) + state_handler.reset.should.be.called() + }) + it('variables are updated in state', function() { - const state_handler = {'set_variable': sinon.spy()} + const state_handler = {'set_variable': sinon.spy(), 'reset': sinon.spy()} const my_default = sinon.spy() call_render_with({ 'parser': (component) => () => component.args.state_handler.set_variable( diff --git a/src/manual_test.js b/src/manual_test.js index db610468..fc5dd805 100644 --- a/src/manual_test.js +++ b/src/manual_test.js @@ -14,6 +14,9 @@ dashboard "Cereals": - dropdown my_var=foo: - {"value": "foo", "text": "Foo"} - {"value": "bar", "text": "Bar"} + - 2 columns: + - p text: "foo \${my_var} bar" + - p text: "foo \${my_var} bar" - bar chart: - attr:query: '{"columns": [(sort_by(-(.calories | tonumber)) | .[] | [.name, .calories])]}' - data: https://gist.githubusercontent.com/ZeningQu/6184eaf8faa533e320abc938c4738c3e/raw/40f237de825061faa8721c2293b79c46979780b4/cereals.csv diff --git a/src/state_handler.js b/src/state_handler.js index 6784394a..7b347073 100644 --- a/src/state_handler.js +++ b/src/state_handler.js @@ -22,6 +22,9 @@ const create_state_handler = () => { const state_handler = { 'get_state': () => state, + 'reset': () => { + subscriptions = [] + }, 'init_variable': (variable, value) => { if (state[variable] === undefined) { handle_change(variable, value) diff --git a/src/state_handler.test.js b/src/state_handler.test.js index ada1ba71..27460a55 100644 --- a/src/state_handler.test.js +++ b/src/state_handler.test.js @@ -70,6 +70,20 @@ describe('state handler', () => { create_state_handler().subscribe.should.be.a.Function() }) + it('has a reset method', () => { + create_state_handler().reset.should.be.a.Function() + }) + + it('reset() undoes subscriptions', () => { + const my_callback = sinon.spy() + const state_handler = create_state_handler() + state_handler.subscribe(my_callback) + state_handler.reset() + state_handler.set_variable('foo', 42) + my_callback.should.not.be.called() + }) + + const methods_that_update_state = ['init_variable', 'set_variable'] methods_that_update_state.forEach(method => {