Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Starting a module that will add it's own html #186

Closed
Wyzix33 opened this issue Dec 11, 2016 · 6 comments
Closed

Starting a module that will add it's own html #186

Wyzix33 opened this issue Dec 11, 2016 · 6 comments

Comments

@Wyzix33
Copy link

Wyzix33 commented Dec 11, 2016

Hi, is it possible to start a module from another module that will add it's own html inside a div?
For example i have a basic html structure
<div data-module="top"></div>
<div id="content"></div>
I want in #content to be able to start different modules based for example on a menu in top module.

So, top module will have to start another module (home or about-us) based on selected menu and that module will have to add it's html <div data-module="home"></div> or <div data-module="about-us"></div> inside #content
or do i have to append the html of the module i want to load inside #content from top module before starting it ?

Thanks

@j3tan
Copy link
Contributor

j3tan commented Dec 12, 2016

There's several themes here, you are talking about routing requests and rendering specific pages based on that. For example, the menu should have links like /home and /about. Then you could use something that responds to the statechange event to:

var contentEl = document.querySelector('#content');

  1. Box.Application.stopAll(contentEl);
  2. contentEl.innerHTML = "<div data-module="about-us">...</div>";
  3. Box.Application.startAll(contentEl);

where the HTML could either come from an AJAX call or you already have it locally. If you want to avoid the router, I suppose you could have 2 modules instead that do something similar

<div data-module="top">
    <button data-type="home-btn">Home</button>
    <button data-type="about-us-btn">About Us</button>
</div>
<div data-module="content">
    <div class="container">
    </div>
</div>

You probably want a helper service too, to help with setting html:

Box.Application.addService('dom', function(application) {
    return {
       setHTML: function(element, html) {
            application.stopAll(element);
            element.innerHTML = html;
            application.startAll(element);
       }  
    }; 
});

Then in the top module:

onclick: function(event, element, elementType) {
   if (elementType === 'home-btn') {
      context.broadcast('navigation', { location: 'home' }); 
   } else if (elementType === 'about-us-btn') {
      context.broadcast('navigation', { location: 'about-us' }); 
   }
}

And in the content module JS:

onmessage: {
   navigate: function(data) {
      var contentEl = document.querySelector('.container', context.getModule());
      var domService = context.getService('dom');
      if (data.location === 'home') {
        domService.setHTML(contentEl, 'some html');
      }
   }
}

At least that's the rough idea.

@Wyzix33
Copy link
Author

Wyzix33 commented Dec 12, 2016

Thank you, it's exactly what I wanted to do.

@Wyzix33 Wyzix33 closed this as completed Dec 12, 2016
@domchristie
Copy link

I have been wondering about starting modules from other modules. Until I saw this issue, I was doing:

element.appendChild(newElement)
context.application.start(newElement) 

However the context.application property is not documented, and so perhaps shouldn't be used in this way(?)

I have created a DOM service which appends the element and starts it, but I am having trouble testing the module because application.start in the service needs to be stubbed out. As far as I can tell, it's not possible to do stub the application object in a module test, only the context.

To keep my tests flexible, I want to test that the element gets appended and module started, so ideally I'd like to keep the DOM service in the allowedServicesList, rather than stub it out and check if its method was called.

I guess this is approach is a bit more like an integration test, but it seems appropriate. So I suppose my question is, is there any way to stub the application object in a module test?

@j3tan
Copy link
Contributor

j3tan commented Dec 14, 2016

Yeah, you shouldn't be using context.application when you can help it. Instead, use your DOM service to start/stop child modules (which is what we do).

@domchristie
Copy link

domchristie commented Dec 15, 2016

OK, cool. So how are you testing that in the module test?

For example, let's say I have a DOM service with an append method:

append: function (element, container) {
  container.append(element)
  application.start(element)
}

In my module:

someMethod: function (newElement) {
  domService.append(newElement, element)
}

In my module test:

element = document.createElement('div')
context = new Box.Application.TestServiceProvider(['dom'])
context.getElement = function () { return element }
module = Box.Application.getModuleForTest('conversation', context)
module.init()

test('someMethod appends the new element', function (assert) {
  var newElement = document.createElement('div')
  module.someMethod(newElement)
  assert.equal(element.firstChild, newElement)
})

This throws an exception that I need to stub out application.start, but I have no way to access the application object in the module test (perhaps rightly so).

I could stub the DOM service and its append method, then check that it is called in the module test, however, I don't really want to do this as it is used quite extensively, and so would require a lot of stubbing! :/

@j3tan
Copy link
Contributor

j3tan commented Dec 15, 2016

First thing I see is
context = new Box.Application.TestServiceProvider(['dom']) should be context = new Box.Application.TestServiceProvider(null, ['dom'])

Since technically you don't want to test the child module's functionality, merely that it was 'started', you could use sandbox.mock(context).expects('start'); or context.start = function() {}; would probably work as well.

The way that the TestServiceProvider works is that it passes itself in as an application stub for services. This should let you stub/mock the start and stop methods

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants