mirror of https://github.com/rancher/ui.git
Merge pull request #17 from vincent99/update-ember
Update ember, cli, handlebars and deprecations
This commit is contained in:
commit
a6cdf94f93
|
|
@ -29,5 +29,5 @@ indent_size = 2
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
[*.md]
|
[*.{diff,md}]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
|
||||||
17
README.md
17
README.md
|
|
@ -8,24 +8,27 @@ Perhaps you like managing cattle.
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
* [Bower](from http://bower.io/)
|
||||||
* [Git](http://git-scm.com/)
|
* [Git](http://git-scm.com/)
|
||||||
* [Node.js](http://nodejs.org/) (with NPM)
|
* [Node.js](http://nodejs.org/) (with NPM)
|
||||||
* [Bower](http://bower.io/)
|
|
||||||
|
|
||||||
If you're on a Mac and use Homebrew, you can follow these steps:
|
If you're on a Mac and use Homebrew, you can follow these steps:
|
||||||
|
```bash
|
||||||
|
brew install node watchman
|
||||||
|
npm install -g bower
|
||||||
|
```
|
||||||
|
|
||||||
|
Setup:
|
||||||
```bash
|
```bash
|
||||||
git clone 'https://github.com/rancherio/ui'
|
git clone 'https://github.com/rancherio/ui'
|
||||||
cd 'ui'
|
cd 'ui'
|
||||||
git submodule init
|
git submodule init
|
||||||
git submodule update
|
git submodule update
|
||||||
brew install node
|
|
||||||
npm install
|
npm install
|
||||||
npm install -g bower
|
|
||||||
bower install
|
bower install
|
||||||
brew install watchman # (or https://facebook.github.io/watchman/docs/install.html)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Run development server
|
Run development server:
|
||||||
```bash
|
```bash
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
@ -46,6 +49,10 @@ The built-in cattle server expects to be run from `/static/` and hosted on a CDN
|
||||||
|
|
||||||
### Running Tests
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g ember-cli
|
||||||
|
```
|
||||||
|
|
||||||
* `ember test`
|
* `ember test`
|
||||||
* `ember test`
|
* `ember test`
|
||||||
* `ember test --server`
|
* `ember test --server`
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import Resolver from 'ember/resolver';
|
import Resolver from 'ember/resolver';
|
||||||
import loadInitializers from 'ember/load-initializers';
|
import loadInitializers from 'ember/load-initializers';
|
||||||
import config from 'ui/config/environment';
|
import config from './config/environment';
|
||||||
|
|
||||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,19 @@ var ApikeyController = Cattle.TransitioningResourceController.extend({
|
||||||
this.transitionToRoute('apikey.delete',this.get('model'));
|
this.transitionToRoute('apikey.delete',this.get('model'));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
availableActions: function() {
|
||||||
|
var a = this.get('actions');
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ tooltip: 'Edit', icon: 'fa-edit', action: 'edit', enabled: !!a.update },
|
||||||
|
{ tooltip: 'Activate', icon: 'fa-play', action: 'activate', enabled: !!a.activate },
|
||||||
|
{ tooltip: 'Deactivate', icon: 'fa-pause', action: 'deactivate', enabled: !!a.deactivate },
|
||||||
|
{ tooltip: 'Restore', icon: 'fa-ambulance', action: 'restore', enabled: !!a.restore },
|
||||||
|
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete' },
|
||||||
|
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
|
||||||
|
];
|
||||||
|
}.property('actions.{update,activate,deactivate,restore,remove,purge}'),
|
||||||
});
|
});
|
||||||
|
|
||||||
ApikeyController.reopenClass({
|
ApikeyController.reopenClass({
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,14 @@ export default Cattle.CollectionController.extend({
|
||||||
needs: ['application'],
|
needs: ['application'],
|
||||||
itemController: 'apikey',
|
itemController: 'apikey',
|
||||||
endpoint: function() {
|
endpoint: function() {
|
||||||
return this.get('controllers.application.absoluteEndpoint').replace(/\/+$/,'') + this.get('app.apiEndpoint');
|
// Strip trailing slash off of the absoluteEndpoint
|
||||||
|
var url = this.get('controllers.application.absoluteEndpoint').replace(/\/+$/,'');
|
||||||
|
// Add a single slash
|
||||||
|
url += '/';
|
||||||
|
|
||||||
|
// And strip leading slashes off the API endpoint
|
||||||
|
url += this.get('app.apiEndpoint').replace(/^\/+/,'');
|
||||||
|
|
||||||
|
return url;
|
||||||
}.property('controllers.application.absoluteEndpoint','app.apiEndpoint')
|
}.property('controllers.application.absoluteEndpoint','app.apiEndpoint')
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
<th width="140"> </th>
|
<th width="140"> </th>
|
||||||
</tr>
|
</tr>
|
||||||
{{#each key in dataSource itemController="apikey"}}
|
{{#each key in dataSource itemController="apikey"}}
|
||||||
<tr>
|
<tr class="resource-action-hover">
|
||||||
<td {{bind-attr class="key.stateColor"}}>
|
<td {{bind-attr class="key.stateColor"}}>
|
||||||
<i {{bind-attr class=":fa key.stateIcon"}}></i> {{key.displayState}}
|
<i {{bind-attr class=":fa key.stateIcon"}}></i> {{key.displayState}}
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -44,12 +44,7 @@
|
||||||
<div>{{#if key.publicValue}}{{key.publicValue}}{{else}}<span class="text-muted text-italic">No public value</span>{{/if}}</div>
|
<div>{{#if key.publicValue}}{{key.publicValue}}{{else}}<span class="text-muted text-italic">No public value</span>{{/if}}</div>
|
||||||
</td>
|
</td>
|
||||||
<td align="right">
|
<td align="right">
|
||||||
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.update::hide"}} {{action "edit"}} tooltip="Edit"><i class="fa fa-edit"></i></button>
|
{{resource-actions model=key}}
|
||||||
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.activate::hide"}} {{action "activate"}} tooltip="Activate"><i class="fa fa-play"></i></button>
|
|
||||||
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.deactivate::hide"}} {{action "deactivate"}} tooltip="Deactivate"><i class="fa fa-pause"></i></button>
|
|
||||||
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.remove::hide"}} {{action "delete"}} tooltip="Delete"><i class="fa fa-trash-o"></i></button>
|
|
||||||
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.restore::hide"}} {{action "restore"}} tooltip="Restore"><i class="fa fa-ambulance"></i></button>
|
|
||||||
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.purge::hide"}} {{action "purge"}} tooltip="Purge"><i class="fa fa-fire"></i></button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
absoluteEndpoint: function() {
|
absoluteEndpoint: function() {
|
||||||
var url = this.get('app.endpoint');
|
var url = this.get('app.endpoint');
|
||||||
|
|
||||||
|
// If the URL is relative, add on the current base URL from the browser
|
||||||
if ( url.indexOf('http') !== 0 )
|
if ( url.indexOf('http') !== 0 )
|
||||||
{
|
{
|
||||||
url = window.location.origin + '/' + url.replace(/^\/+/,'');
|
url = window.location.origin + '/' + url.replace(/^\/+/,'');
|
||||||
|
|
@ -25,6 +27,9 @@ export default Ember.Controller.extend({
|
||||||
url = url + "/";
|
url = url + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Url must end in a single slash
|
||||||
|
url = url.replace(/\/+$/,'') + '/';
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}.property('app.endpoint'),
|
}.property('app.endpoint'),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ export default Ember.Component.extend({
|
||||||
icon: 'fa-square',
|
icon: 'fa-square',
|
||||||
tooltip: '',
|
tooltip: '',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
actionArg: null,
|
||||||
|
altActionArg: null,
|
||||||
|
|
||||||
tagName: 'button',
|
tagName: 'button',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
|
|
@ -12,13 +14,13 @@ export default Ember.Component.extend({
|
||||||
attributeBindings: ['tooltip'],
|
attributeBindings: ['tooltip'],
|
||||||
|
|
||||||
click : function(event) {
|
click : function(event) {
|
||||||
if ( event.altKey )
|
if ( event.altKey && this.get('altActionArg'))
|
||||||
{
|
{
|
||||||
this.sendAction('altAction');
|
this.sendAction('action', this.get('altActionArg'));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.sendAction();
|
this.sendAction('action', this.get('actionArg'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
model: null,
|
||||||
|
classNames: ['instance','resource-action-hover'],
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<div {{bind-attr class=":instance-name model.stateColor"}}>
|
||||||
|
<i {{bind-attr class=":fa model.stateIcon" tooltip=model.displayState}}></i>
|
||||||
|
{{#link-to "container" model.id}}{{model.displayName}}{{/link-to}}
|
||||||
|
</div>
|
||||||
|
{{resource-actions model=model}}
|
||||||
|
|
||||||
|
<div {{bind-attr class="isOn::text-muted"}}>{{model.displayIp}}</div>
|
||||||
|
|
||||||
|
<div {{bind-attr class=":force-wrap model.isError:text-danger:text-muted model.showTransitioningMessage::hide"}}>
|
||||||
|
{{model.transitioningMessage}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div {{bind-attr class="model.isTransitioning::hide :progress :progress-striped :active"}}>
|
||||||
|
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" {{bind-attr aria-valuenow=model.displayProgress style=model.progressStyle}}>
|
||||||
|
<span class="sr-only">{{model.displayProgress}}% Complete</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
model: null,
|
||||||
|
classNames: ['host','resource-action-hover'],
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="host-header resource-action-hover">
|
||||||
|
<div {{bind-attr class=":host-name model.stateColor"}}>
|
||||||
|
<i {{bind-attr class=":fa model.stateIcon" tooltip=model.displayState}}></i>
|
||||||
|
{{#link-to "host.index" model.id}}
|
||||||
|
{{model.displayName}}
|
||||||
|
{{/link-to}}
|
||||||
|
<div class="host-ip">{{model.displayIp}}</div>
|
||||||
|
</div>
|
||||||
|
{{resource-actions model=model}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="no-resource-action-hover">
|
||||||
|
{{#each item in model.instances itemController="container"}}
|
||||||
|
{{container-widget model=item}}
|
||||||
|
{{else}}
|
||||||
|
<div class="text-center text-muted">No containers yet.</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#link-to "hosts.containerNew" (query-params hostId=model.id tab="basic") tagName="div" classNames="add-to-host" tooltip="Create Container"}}
|
||||||
|
<span class="fa-stack fa-lg">
|
||||||
|
<i class="fa fa-circle-thin fa-stack-2x"></i>
|
||||||
|
<i class="fa fa-plus fa-stack-1x"></i>
|
||||||
|
</span>
|
||||||
|
{{/link-to}}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
model: null,
|
||||||
|
isDetail: false,
|
||||||
|
|
||||||
|
classNames: ['resource-actions'],
|
||||||
|
|
||||||
|
activeActions: function() {
|
||||||
|
var detailed = this.get('isDetail');
|
||||||
|
return (this.get('model.availableActions')||[]).filter(function(act) {
|
||||||
|
return Ember.get(act,'enabled') && (detailed || !Ember.get(act,'detail'));
|
||||||
|
});
|
||||||
|
}.property('model.availableActions.[]','model.availableActions.@each.enabled','isDetail'),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
clicked: function(actionName) {
|
||||||
|
this.get('model').send(actionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{{#each item in activeActions}}
|
||||||
|
{{action-button
|
||||||
|
tooltip=item.tooltip
|
||||||
|
icon=item.icon
|
||||||
|
action="clicked"
|
||||||
|
actionArg=item.action
|
||||||
|
altActionArg=item.altAction
|
||||||
|
}}
|
||||||
|
{{/each}}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import Ember from 'ember';
|
||||||
import Cattle from 'ui/utils/cattle';
|
import Cattle from 'ui/utils/cattle';
|
||||||
|
|
||||||
var ContainerController = Cattle.TransitioningResourceController.extend({
|
var ContainerController = Cattle.TransitioningResourceController.extend({
|
||||||
|
|
@ -29,19 +30,46 @@ var ContainerController = Cattle.TransitioningResourceController.extend({
|
||||||
return this.doAction('purge');
|
return this.doAction('purge');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
redirectTo: function(name) {
|
||||||
|
// @TODO Fix this hackery for nested components...
|
||||||
|
// http://emberjs.jsbin.com/mecesakase
|
||||||
|
if ( Ember.Component.detectInstance(this.get('target')) )
|
||||||
|
{
|
||||||
|
this.set('target', window.l('router:main'));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.transitionToRoute(name, this.get('id'));
|
||||||
|
},
|
||||||
|
|
||||||
shell: function() {
|
shell: function() {
|
||||||
this.transitionToRoute('container.shell', this.get('model'));
|
this.send('redirectTo','container.shell');
|
||||||
},
|
},
|
||||||
|
|
||||||
edit: function() {
|
edit: function() {
|
||||||
this.transitionToRoute('container.edit', this.get('model'));
|
this.send('redirectTo','container.edit');
|
||||||
},
|
},
|
||||||
|
|
||||||
promptDelete: function() {
|
promptDelete: function() {
|
||||||
this.transitionToRoute('container.delete', this.get('model'));
|
this.send('redirectTo','container.delete');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
availableActions: function() {
|
||||||
|
var a = this.get('actions');
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ tooltip: 'Edit', icon: 'fa-edit', action: 'edit', enabled: !!a.update },
|
||||||
|
{ tooltip: 'View in API', icon: 'fa-external-link', action: 'goToApi', enabled: true, detail: true },
|
||||||
|
{ tooltip: 'Execute Shell', icon: 'fa-terminal', action: 'shell', enabled: !!a.execute },
|
||||||
|
{ tooltip: 'Restart', icon: 'fa-refresh', action: 'restart', enabled: !!a.restart },
|
||||||
|
{ tooltip: 'Start', icon: 'fa-arrow-up', action: 'start', enabled: !!a.start },
|
||||||
|
{ tooltip: 'Stop', icon: 'fa-arrow-down', action: 'stop', enabled: !!a.stop },
|
||||||
|
{ tooltip: 'Restore', icon: 'fa-ambulance', action: 'restore', enabled: !!a.restore },
|
||||||
|
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: this.get('canDelete'), altAction: 'delete' },
|
||||||
|
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
|
||||||
|
];
|
||||||
|
}.property('actions.{update,execute,restart,start,stop,restore,purge}','canDelete'),
|
||||||
|
|
||||||
isOn: function() {
|
isOn: function() {
|
||||||
return ['running','updating-running','migrating','restarting'].indexOf(this.get('state')) >= 0;
|
return ['running','updating-running','migrating','restarting'].indexOf(this.get('state')) >= 0;
|
||||||
}.property('state'),
|
}.property('state'),
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
{{transitioningMessage}}
|
{{transitioningMessage}}
|
||||||
</div>
|
</div>
|
||||||
<div class="instance-actions">
|
<div class="instance-actions">
|
||||||
{{partial "container/actions"}}
|
{{resource-actions model=this isDetail=true}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,39 @@ import Cattle from 'ui/utils/cattle';
|
||||||
|
|
||||||
var HostController = Cattle.TransitioningResourceController.extend({
|
var HostController = Cattle.TransitioningResourceController.extend({
|
||||||
actions: {
|
actions: {
|
||||||
activate: function() { return this.doAction('activate'); },
|
activate: function() {
|
||||||
deactivate: function() { return this.doAction('deactivate'); },
|
return this.doAction('activate');
|
||||||
delete: function() { return this.delete(); },
|
},
|
||||||
purge: function() { return this.doAction('purge'); },
|
|
||||||
|
deactivate: function() {
|
||||||
|
return this.doAction('deactivate');
|
||||||
|
},
|
||||||
|
|
||||||
|
delete: function() {
|
||||||
|
return this.delete();
|
||||||
|
},
|
||||||
|
|
||||||
|
purge: function() {
|
||||||
|
return this.doAction('purge');
|
||||||
|
},
|
||||||
|
|
||||||
promptDelete: function() {
|
promptDelete: function() {
|
||||||
this.transitionToRoute('host.delete', this.get('model'));
|
this.transitionToRoute('host.delete', this.get('model'));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
availableActions: function() {
|
||||||
|
var a = this.get('actions');
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ tooltip: 'View in API', icon: 'fa-external-link', action: 'goToApi', enabled: true, detail: true },
|
||||||
|
{ tooltip: 'Activate', icon: 'fa-arrow-up', action: 'activate', enabled: !!a.activate },
|
||||||
|
{ tooltip: 'Deactivate', icon: 'fa-arrow-down', action: 'deactivate', enabled: !!a.deactivate },
|
||||||
|
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete' },
|
||||||
|
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
|
||||||
|
];
|
||||||
|
}.property('actions.{activate,deactivate,remove,purge}'),
|
||||||
|
|
||||||
displayIp: function() {
|
displayIp: function() {
|
||||||
var obj = (this.get('ipAddresses')||[]).get('firstObject');
|
var obj = (this.get('ipAddresses')||[]).get('firstObject');
|
||||||
if ( obj )
|
if ( obj )
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<div {{bind-attr class=":instance-name stateColor"}}>
|
|
||||||
<i {{bind-attr class=":fa stateIcon" tooltip=displayState}}></i>
|
|
||||||
{{#link-to "container" this.id}}{{displayName}}{{/link-to}}
|
|
||||||
</div>
|
|
||||||
<div class="instance-actions">
|
|
||||||
{{partial "container/actions"}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div {{bind-attr class="isOn::text-muted"}}>{{displayIp}}</div>
|
|
||||||
|
|
||||||
<div {{bind-attr class=":force-wrap isError:text-danger:text-muted showTransitioningMessage::hide"}}>
|
|
||||||
{{transitioningMessage}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div {{bind-attr class="isTransitioning::hide :progress :progress-striped :active"}}>
|
|
||||||
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" {{bind-attr aria-valuenow=displayProgress style=progressStyle}}>
|
|
||||||
<span class="sr-only">{{displayProgress}}% Complete</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.View.extend({
|
|
||||||
classNames: ['instance'],
|
|
||||||
tagName: 'DIV',
|
|
||||||
templateName: 'host/instance',
|
|
||||||
});
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
{{transitioningMessage}}
|
{{transitioningMessage}}
|
||||||
</div>
|
</div>
|
||||||
<div class="host-actions">
|
<div class="host-actions">
|
||||||
{{partial "host/actions"}}
|
{{resource-actions model=this}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -44,7 +44,9 @@
|
||||||
<label>Containers</label>
|
<label>Containers</label>
|
||||||
{{#each col in view.columns}}
|
{{#each col in view.columns}}
|
||||||
<div class="instance-column">
|
<div class="instance-column">
|
||||||
{{each col itemViewClass="host/instance" itemController="container"}}
|
{{#each item in col itemController="container"}}
|
||||||
|
{{container-widget model=item}}
|
||||||
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div style="padding: 20px; text-align: center;" class="text-muted">
|
<div style="padding: 20px; text-align: center;" class="text-muted">
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@
|
||||||
{{each host.instances itemViewClass="instanceDot" itemController="container"}}
|
{{each host.instances itemViewClass="instanceDot" itemController="container"}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{each col itemViewClass="hosts/item" itemController="host"}}
|
{{#each host in col itemController="host"}}
|
||||||
|
{{host-widget model=host}}
|
||||||
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<div class="host-header">
|
|
||||||
<div {{bind-attr class=":host-name stateColor"}}>
|
|
||||||
<i {{bind-attr class=":fa stateIcon" tooltip="displayState"}}></i>
|
|
||||||
{{#link-to "host.index" this.id}}
|
|
||||||
{{displayName}}
|
|
||||||
{{/link-to}}
|
|
||||||
<div class="host-ip">{{displayIp}}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="host-actions">
|
|
||||||
{{partial "host/actions"}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#each instances itemController="container"}}
|
|
||||||
{{view "host/instance"}}
|
|
||||||
{{else}}
|
|
||||||
<div class="text-center text-muted">No containers yet.</div>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#link-to "hosts.containerNew" (query-params hostId=this.id tab="basic") tagName="div" classNames="add-to-host" tooltip="Create Container"}}
|
|
||||||
<span class="fa-stack fa-lg">
|
|
||||||
<i class="fa fa-circle-thin fa-stack-2x"></i>
|
|
||||||
<i class="fa fa-plus fa-stack-1x"></i>
|
|
||||||
</span>
|
|
||||||
{{/link-to}}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.View.extend({
|
|
||||||
classNames: ['host'],
|
|
||||||
templateName: 'hosts/item',
|
|
||||||
});
|
|
||||||
|
|
@ -22,6 +22,16 @@ var VolumeController = Cattle.TransitioningResourceController.extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
availableActions: function() {
|
||||||
|
var a = this.get('actions');
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ tooltip: 'Restore', icon: 'fa-ambulance', action: 'restore', enabled: !!a.restore },
|
||||||
|
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: this.get('canDelete'), altAction: 'delete' },
|
||||||
|
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
|
||||||
|
];
|
||||||
|
}.property('actions.{restore,purge}','canDelete'),
|
||||||
|
|
||||||
displayUri: function() {
|
displayUri: function() {
|
||||||
return (this.get('uri')||'').replace(/^file:\/\//,'');
|
return (this.get('uri')||'').replace(/^file:\/\//,'');
|
||||||
}.property('uri'),
|
}.property('uri'),
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,50 @@
|
||||||
<section>
|
<section>
|
||||||
<table class="table fixed" style="margin-bottom: 0">
|
<table class="table fixed" style="margin-bottom: 0">
|
||||||
<tr>
|
<tbody>
|
||||||
<th width="120">State</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Containers</th>
|
|
||||||
<th>Created</th>
|
|
||||||
<th width="140"> </th>
|
|
||||||
</tr>
|
|
||||||
<tr class="no-lines">
|
|
||||||
<th colspan="5" class="text-muted">Host Path
|
|
||||||
</tr>
|
|
||||||
{{#each volume in nonRootVolumes itemController="volume"}}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td {{bind-attr class="volume.stateColor"}}>
|
<th width="120">State</th>
|
||||||
<i {{bind-attr class=":fa volume.stateIcon"}}></i> {{volume.displayState}}
|
<th>Name</th>
|
||||||
</td>
|
<th>Containers</th>
|
||||||
|
<th>Created</th>
|
||||||
<td>
|
<th width="140"> </th>
|
||||||
{{volume.displayName}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
{{#if volume.activeMounts.length}}
|
|
||||||
{{volume.activeMounts.length}}
|
|
||||||
{{else}}
|
|
||||||
<span class="text-muted">None</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<div class="text-muted">{{date-calendar volume.created}}</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td align="right">
|
|
||||||
<button type="button" {{bind-attr class=":btn :btn-sm :btn-info volume.canDelete::hide"}} {{action "promptDelete"}} tooltip="Delete"><i class="fa fa-trash-o"></i></button>
|
|
||||||
<button type="button" {{bind-attr class=":btn :btn-sm :btn-info volume.actions.restore::hide"}} {{action "restore"}} tooltip="Restore"><i class="fa fa-ambulance"></i></button>
|
|
||||||
<button type="button" {{bind-attr class=":btn :btn-sm :btn-info volume.actions.purge::hide"}} {{action "purge"}} tooltip="Purge"><i class="fa fa-fire"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="no-lines">
|
<tr class="no-lines">
|
||||||
<td colspan="5" class="text-muted clip">
|
<th colspan="5" class="text-muted">Host Path
|
||||||
{{volume.displayUri}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{{#each volume in nonRootVolumes itemController="volume"}}
|
||||||
|
<tbody class="resource-action-hover">
|
||||||
|
<tr>
|
||||||
|
<td {{bind-attr class="volume.stateColor"}}>
|
||||||
|
<i {{bind-attr class=":fa volume.stateIcon"}}></i> {{volume.displayState}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{volume.displayName}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{#if volume.activeMounts.length}}
|
||||||
|
{{volume.activeMounts.length}}
|
||||||
|
{{else}}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<div class="text-muted">{{date-calendar volume.created}}</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td align="right" rowspan="2">
|
||||||
|
{{resource-actions model=volume}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-lines">
|
||||||
|
<td colspan="4" class="text-muted clip">
|
||||||
|
{{volume.displayUri}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
@import "app/styles/growl";
|
@import "app/styles/growl";
|
||||||
|
|
||||||
|
@import "app/styles/resource-actions";
|
||||||
@import "app/styles/layout";
|
@import "app/styles/layout";
|
||||||
@import "app/styles/overlay";
|
@import "app/styles/overlay";
|
||||||
@import "app/styles/progress-bar";
|
@import "app/styles/progress-bar";
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,8 @@ H1, H2, H3, H4, H5, H6 {
|
||||||
.table.no-lines > TBODY > TR > TH,
|
.table.no-lines > TBODY > TR > TH,
|
||||||
.table.no-lines > TBODY > TR > TD,
|
.table.no-lines > TBODY > TR > TD,
|
||||||
.table.no-lines > TFOOT > TR > TH,
|
.table.no-lines > TFOOT > TR > TH,
|
||||||
.table.no-lines > TFOOT > TR > TD {
|
.table.no-lines > TFOOT > TR > TD,
|
||||||
|
.table > TBODY + TBODY {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,33 +99,6 @@ $instance-column-width: $host-column-width - 20px;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.host .host-actions,
|
|
||||||
.instance .instance-actions {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.host-actions,
|
|
||||||
.instance-actions {
|
|
||||||
BUTTON {
|
|
||||||
color: #444;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BUTTON:hover {
|
|
||||||
color: #1d6fa5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance-actions {
|
|
||||||
padding: 5px 10px 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance-detail .instance-actions {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance {
|
.instance {
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid #e7eaee;
|
border: 1px solid #e7eaee;
|
||||||
|
|
@ -158,7 +131,7 @@ $instance-column-width: $host-column-width - 20px;
|
||||||
-webkit-animation: progress-bar-stripes-reverse 1.5s linear infinite;
|
-webkit-animation: progress-bar-stripes-reverse 1.5s linear infinite;
|
||||||
animation: progress-bar-stripes-reverse 1.5s linear infinite;
|
animation: progress-bar-stripes-reverse 1.5s linear infinite;
|
||||||
|
|
||||||
background-image: -webkit-linear-gradient(45deg,
|
background-image: -webkit-linear-gradient(45deg,
|
||||||
rgba(255, 255, 255, 0.55) 25%,
|
rgba(255, 255, 255, 0.55) 25%,
|
||||||
transparent 25%,
|
transparent 25%,
|
||||||
transparent 50%,
|
transparent 50%,
|
||||||
|
|
@ -179,22 +152,6 @@ $instance-column-width: $host-column-width - 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hover actions on devices with mice, always shown on devices with touch */
|
|
||||||
.no-touch {
|
|
||||||
.host-actions,
|
|
||||||
.instance-actions {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.host:hover .host-actions,
|
|
||||||
.instance:hover .instance-actions,
|
|
||||||
.host-detail .host-actions,
|
|
||||||
.instance-detail .instance-actions {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.dot {
|
.dot {
|
||||||
float: left;
|
float: left;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
|
|
@ -218,3 +175,14 @@ $instance-column-width: $host-column-width - 20px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.host .resource-actions,
|
||||||
|
.instance .resource-actions {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance .resource-actions {
|
||||||
|
padding: 5px 10px 5px 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
.resource-actions {
|
||||||
|
BUTTON {
|
||||||
|
color: #444;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BUTTON:hover {
|
||||||
|
color: #1d6fa5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hover actions on devices with mice, always shown on devices with touch.
|
||||||
|
|
||||||
|
Put .resource-action-hover on a parent node that you want to trigger display of the actions on.
|
||||||
|
|
||||||
|
If there are nested nodes with actions, put .no-resource-action-hover on a node that wraps the inner nodes
|
||||||
|
to prevent hover on the parent from triggering hover on the children.
|
||||||
|
*/
|
||||||
|
.no-touch {
|
||||||
|
.resource-action-hover {
|
||||||
|
.resource-actions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.resource-actions {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-resource-action-hover {
|
||||||
|
.resource-actions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-resource-action-hover {
|
||||||
|
.resource-action-hover:hover {
|
||||||
|
.resource-actions {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{{action-button enabled=actions.update tooltip="Edit" icon="fa-edit" action="edit"}}
|
|
||||||
{{action-button enabled=view.isDetail tooltip="View in API" icon="fa-external-link" action="goToApi"}}
|
|
||||||
{{action-button enabled=actions.execute tooltip="Execute Shell" icon="fa-terminal" action="shell"}}
|
|
||||||
{{action-button enabled=actions.restart tooltip="Restart" icon="fa-refresh" action="restart"}}
|
|
||||||
{{action-button enabled=actions.start tooltip="Start" icon="fa-arrow-up" action="start"}}
|
|
||||||
{{action-button enabled=actions.stop tooltip="Stop" icon="fa-arrow-down" action="stop"}}
|
|
||||||
{{action-button enabled=actions.restore tooltip="Restore" icon="fa-ambulance" action="restore"}}
|
|
||||||
{{action-button enabled=canDelete tooltip="Delete" icon="fa-trash-o" action="promptDelete" altAction="delete"}}
|
|
||||||
{{action-button enabled=actions.purge tooltip="Purge" icon="fa-fire" action="purge"}}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
{{action-button enabled=view.isDetail tooltip="View in API" icon="fa-external-link" action="goToApi"}}
|
|
||||||
{{action-button enabled=actions.activate tooltip="Activate" icon="fa-arrow-up" action="activate"}}
|
|
||||||
{{action-button enabled=actions.deactivate tooltip="Deactivate" icon="fa-arrow-down" action="deactivate"}}
|
|
||||||
{{action-button enabled=actions.remove tooltip="Delete" icon="fa-trash-o" action="promptDelete" altAction="delete"}}
|
|
||||||
{{action-button enabled=actions.purge tooltip="Purge" icon="fa-fire" action="purge"}}
|
|
||||||
|
|
@ -9,12 +9,12 @@
|
||||||
<th width="150">Size</th>
|
<th width="150">Size</th>
|
||||||
<th>Usage</th>
|
<th>Usage</th>
|
||||||
</tr>
|
</tr>
|
||||||
{{#each view.stats.filesystem}}
|
{{#each fs in view.stats.filesystem}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{device}}</td>
|
<td>{{fs.device}}</td>
|
||||||
<td>{{size_gb}} GB</td>
|
<td>{{fs.size_gb}} GB</td>
|
||||||
<td>
|
<td>
|
||||||
{{progress-bar value=used_percent textSuffix="% Used"}}
|
{{progress-bar value=fs.used_percent textSuffix="% Used"}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,24 @@ var TransitioningResourceController = ResourceController.extend({
|
||||||
return model.doAction.apply(model,arguments);
|
return model.doAction.apply(model,arguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
availableActions: function() {
|
||||||
|
/*
|
||||||
|
Override me and return [
|
||||||
|
{
|
||||||
|
enabled: true/false, // Whether it's enabled or greyed out
|
||||||
|
detail: true/false, // If true, this action will only be shown on detailed screens
|
||||||
|
tooltip: 'Delete', // Tooltip shown on hover
|
||||||
|
icon: 'fa-trash-o', // Icon shown on screen
|
||||||
|
action: 'promptDelete', // Action to call on the controller when clicked
|
||||||
|
altAction: 'delete' // Action to call on the controller when alt+clicked
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
displayProgress: function() {
|
displayProgress: function() {
|
||||||
var progress = this.get('transitioningProgress');
|
var progress = this.get('transitioningProgress');
|
||||||
if ( isNaN(progress) || !progress )
|
if ( isNaN(progress) || !progress )
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ui",
|
"name": "ui",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"handlebars": "~1.3.0",
|
"ember": "1.9.1",
|
||||||
|
"handlebars": "2.0.0",
|
||||||
"jquery": "^1.11.1",
|
"jquery": "^1.11.1",
|
||||||
"ember": "1.8.1",
|
|
||||||
"ember-data": "1.0.0-beta.12",
|
"ember-data": "1.0.0-beta.12",
|
||||||
"ember-resolver": "~0.1.10",
|
"ember-resolver": "~0.1.10",
|
||||||
"loader.js": "stefanpenner/loader.js#1.0.1",
|
"loader.js": "stefanpenner/loader.js#1.0.1",
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,17 @@
|
||||||
"author": "Rancher Labs",
|
"author": "Rancher Labs",
|
||||||
"license": "Apache 2",
|
"license": "Apache 2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ember-api-store": "^1.0.6",
|
|
||||||
|
|
||||||
"body-parser": "^1.2.0",
|
"body-parser": "^1.2.0",
|
||||||
"broccoli-asset-rev": "^1.0.0",
|
"broccoli-asset-rev": "^1.0.0",
|
||||||
"broccoli-ember-hbs-template-compiler": "1.6.2",
|
|
||||||
"broccoli-sass": "^0.3.3",
|
"broccoli-sass": "^0.3.3",
|
||||||
"connect-restreamer": "^1.0.1",
|
"connect-restreamer": "^1.0.1",
|
||||||
"ember-cli": "^0.1.4",
|
"ember-api-store": "^1.0.6",
|
||||||
|
"ember-cli": "^0.1.5",
|
||||||
"ember-cli-content-security-policy": "0.3.0",
|
"ember-cli-content-security-policy": "0.3.0",
|
||||||
"ember-cli-dependency-checker": "0.0.6",
|
"ember-cli-dependency-checker": "0.0.7",
|
||||||
"ember-cli-esnext": "0.1.1",
|
"ember-cli-esnext": "0.1.1",
|
||||||
"ember-cli-font-awesome": "0.0.4",
|
"ember-cli-font-awesome": "0.0.4",
|
||||||
|
"ember-cli-htmlbars": "^0.6.0",
|
||||||
"ember-cli-inject-live-reload": "^1.3.0",
|
"ember-cli-inject-live-reload": "^1.3.0",
|
||||||
"ember-cli-inline-content": "^0.3.1",
|
"ember-cli-inline-content": "^0.3.1",
|
||||||
"ember-cli-moment": "0.0.1",
|
"ember-cli-moment": "0.0.1",
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,16 @@ import Router from '../../router';
|
||||||
import config from '../../config/environment';
|
import config from '../../config/environment';
|
||||||
|
|
||||||
export default function startApp(attrs) {
|
export default function startApp(attrs) {
|
||||||
var App;
|
var application;
|
||||||
|
|
||||||
var attributes = Ember.merge({}, config.APP);
|
var attributes = Ember.merge({}, config.APP);
|
||||||
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
|
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
|
||||||
|
|
||||||
Ember.run(function() {
|
Ember.run(function() {
|
||||||
App = Application.create(attributes);
|
application = Application.create(attributes);
|
||||||
App.setupForTesting();
|
application.setupForTesting();
|
||||||
App.injectTestHelpers();
|
application.injectTestHelpers();
|
||||||
});
|
});
|
||||||
|
|
||||||
return App;
|
return application;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import {
|
||||||
|
moduleForComponent,
|
||||||
|
test
|
||||||
|
} from 'ember-qunit';
|
||||||
|
|
||||||
|
moduleForComponent('resource-actions', 'ResourceActionsComponent', {
|
||||||
|
// specify the other units that are required for this test
|
||||||
|
// needs: ['component:foo', 'helper:bar']
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders', function() {
|
||||||
|
expect(2);
|
||||||
|
|
||||||
|
// creates the component instance
|
||||||
|
var component = this.subject();
|
||||||
|
equal(component._state, 'preRender');
|
||||||
|
|
||||||
|
// appends the component to the page
|
||||||
|
this.append();
|
||||||
|
equal(component._state, 'inDOM');
|
||||||
|
});
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import {
|
|
||||||
moduleFor,
|
|
||||||
test
|
|
||||||
} from 'ember-qunit';
|
|
||||||
|
|
||||||
moduleFor('view:host/instance', 'HostInstanceView');
|
|
||||||
|
|
||||||
// Replace this with your real tests.
|
|
||||||
test('it exists', function() {
|
|
||||||
var view = this.subject();
|
|
||||||
ok(view);
|
|
||||||
});
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import {
|
|
||||||
moduleFor,
|
|
||||||
test
|
|
||||||
} from 'ember-qunit';
|
|
||||||
|
|
||||||
moduleFor('view:hosts/item', 'HostsItemView');
|
|
||||||
|
|
||||||
// Replace this with your real tests.
|
|
||||||
test('it exists', function() {
|
|
||||||
var view = this.subject();
|
|
||||||
ok(view);
|
|
||||||
});
|
|
||||||
Loading…
Reference in New Issue