mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-24 06:22:45 +08:00
refactor(nav): create NavControllerBase and public abstract class
Use NavController as the public API, and NavControllerBase as the internal API. Refactored all app/nav/tabs unit tests and created centralized mocking functions.
This commit is contained in:
@ -4,6 +4,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { ClickBlock } from '../../util/click-block';
|
import { ClickBlock } from '../../util/click-block';
|
||||||
import { Config } from '../../config/config';
|
import { Config } from '../../config/config';
|
||||||
import { NavController } from '../nav/nav-controller';
|
import { NavController } from '../nav/nav-controller';
|
||||||
|
import { isTabs, isNav } from '../nav/nav-controller-base';
|
||||||
import { NavOptions } from '../nav/nav-interfaces';
|
import { NavOptions } from '../nav/nav-interfaces';
|
||||||
import { NavPortal } from '../nav/nav-portal';
|
import { NavPortal } from '../nav/nav-portal';
|
||||||
import { Platform } from '../../platform/platform';
|
import { Platform } from '../../platform/platform';
|
||||||
@ -195,13 +196,7 @@ export class App {
|
|||||||
// function used to climb up all parent nav controllers
|
// function used to climb up all parent nav controllers
|
||||||
function navPop(nav: any): Promise<any> {
|
function navPop(nav: any): Promise<any> {
|
||||||
if (nav) {
|
if (nav) {
|
||||||
if (nav.length && nav.length() > 1) {
|
if (isTabs(nav)) {
|
||||||
// this nav controller has more than one view
|
|
||||||
// pop the current view on this nav and we're done here
|
|
||||||
console.debug('app, goBack pop nav');
|
|
||||||
return nav.pop();
|
|
||||||
|
|
||||||
} else if (nav.previousTab) {
|
|
||||||
// FYI, using "nav instanceof Tabs" throws a Promise runtime error for whatever reason, idk
|
// FYI, using "nav instanceof Tabs" throws a Promise runtime error for whatever reason, idk
|
||||||
// this is a Tabs container
|
// this is a Tabs container
|
||||||
// see if there is a valid previous tab to go to
|
// see if there is a valid previous tab to go to
|
||||||
@ -211,6 +206,12 @@ export class App {
|
|||||||
nav.select(prevTab);
|
nav.select(prevTab);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (isNav(nav) && nav.length() > 1) {
|
||||||
|
// this nav controller has more than one view
|
||||||
|
// pop the current view on this nav and we're done here
|
||||||
|
console.debug('app, goBack pop nav');
|
||||||
|
return nav.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// try again using the parent nav (if there is one)
|
// try again using the parent nav (if there is one)
|
||||||
@ -244,10 +245,9 @@ export class App {
|
|||||||
console.debug('app, goBack exitApp');
|
console.debug('app, goBack exitApp');
|
||||||
this._platform.exitApp();
|
this._platform.exitApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
return navPromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return navPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import {Component} from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import {App, Nav, Tabs, Tab, NavOptions, Config, ViewController, Platform} from '../../../../src';
|
import { App, Config, Nav, NavOptions, Platform, Tab, Tabs, ViewController } from '../../../../src';
|
||||||
|
import { mockNavController, mockTab, mockTabs } from '../../../../src/util/mock-providers';
|
||||||
|
|
||||||
export function run() {
|
export function run() {
|
||||||
|
|
||||||
|
|
||||||
describe('App', () => {
|
describe('App', () => {
|
||||||
|
|
||||||
describe('navPop', () => {
|
describe('navPop', () => {
|
||||||
|
|
||||||
it('should select the previous tab', () => {
|
it('should select the previous tab', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
@ -40,8 +40,8 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should pop from the active tab, when tabs is nested is the root nav', () => {
|
it('should pop from the active tab, when tabs is nested is the root nav', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
@ -91,9 +91,9 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should pop the root nav when nested nav has less than 2 views', () => {
|
it('should pop the root nav when nested nav has less than 2 views', () => {
|
||||||
let rootNav = mockNav();
|
let rootNav = mockNavController();
|
||||||
let nestedNav = mockNav();
|
let nestedNav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
rootNav.registerChildNav(nestedNav);
|
rootNav.registerChildNav(nestedNav);
|
||||||
nestedNav.parent = rootNav;
|
nestedNav.parent = rootNav;
|
||||||
@ -120,9 +120,9 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should pop a view from the nested nav that has more than 1 view', () => {
|
it('should pop a view from the nested nav that has more than 1 view', () => {
|
||||||
let rootNav = mockNav();
|
let rootNav = mockNavController();
|
||||||
let nestedNav = mockNav();
|
let nestedNav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(rootNav);
|
app.setRootNav(rootNav);
|
||||||
rootNav.registerChildNav(nestedNav);
|
rootNav.registerChildNav(nestedNav);
|
||||||
@ -149,8 +149,8 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should pop the overlay in the portal of the root nav', () => {
|
it('should pop the overlay in the portal of the root nav', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
@ -173,8 +173,8 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should pop the second view in the root nav', () => {
|
it('should pop the second view in the root nav', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
@ -194,8 +194,8 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should exit app when only one view in the root nav', () => {
|
it('should exit app when only one view in the root nav', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
@ -217,8 +217,8 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not exit app when only one view in the root nav, but navExitApp config set', () => {
|
it('should not exit app when only one view in the root nav, but navExitApp config set', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
@ -242,8 +242,8 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not go back if app is not enabled', () => {
|
it('should not go back if app is not enabled', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
let portal = mockNav();
|
let portal = mockNavController();
|
||||||
app.setPortal(portal);
|
app.setPortal(portal);
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ describe('App', () => {
|
|||||||
describe('getActiveNav', () => {
|
describe('getActiveNav', () => {
|
||||||
|
|
||||||
it('should get active NavController when using tabs with nested nav', () => {
|
it('should get active NavController when using tabs with nested nav', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
let tabs = mockTabs();
|
let tabs = mockTabs();
|
||||||
@ -285,9 +285,9 @@ describe('App', () => {
|
|||||||
nav.registerChildNav(tabs);
|
nav.registerChildNav(tabs);
|
||||||
|
|
||||||
tab2.setSelected(true);
|
tab2.setSelected(true);
|
||||||
let nav2 = mockNav();
|
let nav2 = mockNavController();
|
||||||
let nav3 = mockNav();
|
let nav3 = mockNavController();
|
||||||
let nav4 = mockNav();
|
let nav4 = mockNavController();
|
||||||
tab1.registerChildNav(nav4);
|
tab1.registerChildNav(nav4);
|
||||||
tab2.registerChildNav(nav2);
|
tab2.registerChildNav(nav2);
|
||||||
tab2.registerChildNav(nav3);
|
tab2.registerChildNav(nav3);
|
||||||
@ -296,7 +296,7 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get active NavController when using tabs, nested in a root nav', () => {
|
it('should get active NavController when using tabs, nested in a root nav', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
|
|
||||||
let tabs = mockTabs();
|
let tabs = mockTabs();
|
||||||
@ -331,9 +331,9 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get active NavController when nested 3 deep', () => {
|
it('should get active NavController when nested 3 deep', () => {
|
||||||
let nav1 = mockNav();
|
let nav1 = mockNavController();
|
||||||
let nav2 = mockNav();
|
let nav2 = mockNavController();
|
||||||
let nav3 = mockNav();
|
let nav3 = mockNavController();
|
||||||
app.setRootNav(nav1);
|
app.setRootNav(nav1);
|
||||||
|
|
||||||
nav1.registerChildNav(nav2);
|
nav1.registerChildNav(nav2);
|
||||||
@ -343,8 +343,8 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get active NavController when nested 2 deep', () => {
|
it('should get active NavController when nested 2 deep', () => {
|
||||||
let nav1 = mockNav();
|
let nav1 = mockNavController();
|
||||||
let nav2 = mockNav();
|
let nav2 = mockNavController();
|
||||||
app.setRootNav(nav1);
|
app.setRootNav(nav1);
|
||||||
|
|
||||||
nav1.registerChildNav(nav2);
|
nav1.registerChildNav(nav2);
|
||||||
@ -352,13 +352,13 @@ describe('App', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get active NavController when only one nav controller', () => {
|
it('should get active NavController when only one nav controller', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
expect(app.getActiveNav()).toBe(nav);
|
expect(app.getActiveNav()).toBe(nav);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set/get the root nav controller', () => {
|
it('should set/get the root nav controller', () => {
|
||||||
let nav = mockNav();
|
let nav = mockNavController();
|
||||||
app.setRootNav(nav);
|
app.setRootNav(nav);
|
||||||
expect(app.getRootNav()).toBe(nav);
|
expect(app.getRootNav()).toBe(nav);
|
||||||
});
|
});
|
||||||
@ -443,40 +443,13 @@ describe('App', () => {
|
|||||||
var app: App;
|
var app: App;
|
||||||
var config: Config;
|
var config: Config;
|
||||||
var platform: Platform;
|
var platform: Platform;
|
||||||
var _cd: any;
|
|
||||||
|
|
||||||
function mockNav(): Nav {
|
|
||||||
return new Nav(null, null, null, config, null, null, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mockTabs(): Tabs {
|
|
||||||
return new Tabs(null, null, null, config, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mockTab(parentTabs: Tabs): Tab {
|
|
||||||
var tab = new Tab(parentTabs, app, config, null, null, null, null, null, _cd, null);
|
|
||||||
parentTabs.add(tab);
|
|
||||||
tab.root = SomePage;
|
|
||||||
tab.load = function(opts: any, cb: Function) {
|
|
||||||
cb();
|
|
||||||
};
|
|
||||||
return tab;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({})
|
|
||||||
class SomePage {}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = new Config();
|
config = new Config();
|
||||||
platform = new Platform();
|
platform = new Platform();
|
||||||
app = new App(config, platform);
|
app = new App(config, platform);
|
||||||
_cd = {
|
|
||||||
reattach: function(){},
|
|
||||||
detach: function(){}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import { isTrueProperty } from '../../util/util';
|
|||||||
import { Item } from '../item/item';
|
import { Item } from '../item/item';
|
||||||
import { NativeInput, NextInput } from './native-input';
|
import { NativeInput, NextInput } from './native-input';
|
||||||
import { NavController } from '../nav/nav-controller';
|
import { NavController } from '../nav/nav-controller';
|
||||||
|
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||||
import { Platform } from '../../platform/platform';
|
import { Platform } from '../../platform/platform';
|
||||||
|
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ export class InputBase {
|
|||||||
protected _autoFocusAssist: string;
|
protected _autoFocusAssist: string;
|
||||||
protected _autoComplete: string;
|
protected _autoComplete: string;
|
||||||
protected _autoCorrect: string;
|
protected _autoCorrect: string;
|
||||||
|
protected _nav: NavControllerBase;
|
||||||
|
|
||||||
inputControl: NgControl;
|
inputControl: NgControl;
|
||||||
|
|
||||||
@ -44,9 +46,10 @@ export class InputBase {
|
|||||||
protected _platform: Platform,
|
protected _platform: Platform,
|
||||||
protected _elementRef: ElementRef,
|
protected _elementRef: ElementRef,
|
||||||
protected _scrollView: Content,
|
protected _scrollView: Content,
|
||||||
protected _nav: NavController,
|
nav: NavController,
|
||||||
ngControl: NgControl
|
ngControl: NgControl
|
||||||
) {
|
) {
|
||||||
|
this._nav = <NavControllerBase>nav;
|
||||||
this._useAssist = config.getBoolean('scrollAssist', false);
|
this._useAssist = config.getBoolean('scrollAssist', false);
|
||||||
this._usePadding = config.getBoolean('scrollPadding', this._useAssist);
|
this._usePadding = config.getBoolean('scrollPadding', this._useAssist);
|
||||||
this._keyboardHeight = config.getNumber('keyboardHeight');
|
this._keyboardHeight = config.getNumber('keyboardHeight');
|
||||||
|
1303
src/components/nav/nav-controller-base.ts
Normal file
1303
src/components/nav/nav-controller-base.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -13,3 +13,6 @@ export interface NavOptions {
|
|||||||
climbNav?: boolean;
|
climbNav?: boolean;
|
||||||
ev?: any;
|
ev?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const DIRECTION_BACK = 'back';
|
||||||
|
export const DIRECTION_FORWARD = 'forward';
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { Directive, Optional } from '@angular/core';
|
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||||
import { NavController } from './nav-controller';
|
import { NavController } from './nav-controller';
|
||||||
|
import { noop } from '../../util/util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name NavPop
|
* @name NavPop
|
||||||
* @description
|
* @description
|
||||||
* Directive for declaratively pop the current page off from the navigation stack.
|
* Directive to declaratively pop the current page off from the
|
||||||
|
* navigation stack.
|
||||||
*
|
*
|
||||||
* @usage
|
* @usage
|
||||||
* ```html
|
* ```html
|
||||||
@ -22,11 +23,7 @@ import { NavController } from './nav-controller';
|
|||||||
* @see {@link ../NavPush NavPush API Docs}
|
* @see {@link ../NavPush NavPush API Docs}
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[nav-pop]',
|
selector: '[navPop]'
|
||||||
host: {
|
|
||||||
'(click)': 'onClick()',
|
|
||||||
'role': 'link'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
export class NavPop {
|
export class NavPop {
|
||||||
|
|
||||||
@ -36,10 +33,15 @@ export class NavPop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@HostListener('click')
|
||||||
* @private
|
onClick(): boolean {
|
||||||
*/
|
// If no target, or if target is _self, prevent default browser behavior
|
||||||
onClick() {
|
if (this._nav) {
|
||||||
this._nav && this._nav.pop();
|
this._nav.pop(null, noop);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { App } from '../app/app';
|
|||||||
import { Config } from '../../config/config';
|
import { Config } from '../../config/config';
|
||||||
import { GestureController } from '../../gestures/gesture-controller';
|
import { GestureController } from '../../gestures/gesture-controller';
|
||||||
import { Keyboard } from '../../util/keyboard';
|
import { Keyboard } from '../../util/keyboard';
|
||||||
import { NavController } from '../nav/nav-controller';
|
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -12,7 +12,7 @@ import { NavController } from '../nav/nav-controller';
|
|||||||
@Directive({
|
@Directive({
|
||||||
selector: '[nav-portal]'
|
selector: '[nav-portal]'
|
||||||
})
|
})
|
||||||
export class NavPortal extends NavController {
|
export class NavPortal extends NavControllerBase {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => App)) app: App,
|
@Inject(forwardRef(() => App)) app: App,
|
||||||
config: Config,
|
config: Config,
|
||||||
@ -25,7 +25,7 @@ export class NavPortal extends NavController {
|
|||||||
viewPort: ViewContainerRef
|
viewPort: ViewContainerRef
|
||||||
) {
|
) {
|
||||||
super(null, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
super(null, app, config, keyboard, elementRef, zone, renderer, compiler, gestureCtrl);
|
||||||
this.isPortal = true;
|
this._isPortal = true;
|
||||||
this.setViewport(viewPort);
|
this.setViewport(viewPort);
|
||||||
app.setPortal(this);
|
app.setPortal(this);
|
||||||
|
|
||||||
|
@ -1,28 +1,35 @@
|
|||||||
import { Directive, Input, Optional } from '@angular/core';
|
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||||
|
|
||||||
import { NavController } from './nav-controller';
|
import { NavController } from './nav-controller';
|
||||||
|
import { noop } from '../../util/util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name NavPush
|
* @name NavPush
|
||||||
* @description
|
* @description
|
||||||
* Directive for declaratively linking to a new page instead of using
|
* Directive to declaratively push a new page to the current nav
|
||||||
* {@link ../NavController/#push NavController.push}. Similar to ui-router's `ui-sref`.
|
* stack.
|
||||||
*
|
*
|
||||||
* @usage
|
* @usage
|
||||||
* ```html
|
* ```html
|
||||||
* <button [navPush]="pushPage"></button>
|
* <button [navPush]="pushPage"></button>
|
||||||
* ```
|
* ```
|
||||||
* To specify parameters you can use array syntax or the `nav-params` property:
|
*
|
||||||
|
* To specify parameters you can use array syntax or the `navParams`
|
||||||
|
* property:
|
||||||
|
*
|
||||||
* ```html
|
* ```html
|
||||||
* <button [navPush]="pushPage" [navParams]="params"></button>
|
* <button [navPush]="pushPage" [navParams]="params">Go</button>
|
||||||
* ```
|
* ```
|
||||||
* Where `pushPage` and `params` are specified in your component, and `pushPage`
|
*
|
||||||
* contains a reference to a [@Page component](../../../config/Page/):
|
* Where `pushPage` and `params` are specified in your component,
|
||||||
|
* and `pushPage` contains a reference to a
|
||||||
|
* [@Page component](../../../config/Page/):
|
||||||
*
|
*
|
||||||
* ```ts
|
* ```ts
|
||||||
* import {LoginPage} from 'login';
|
* import { LoginPage } from './login';
|
||||||
|
*
|
||||||
* @Component({
|
* @Component({
|
||||||
* template: `<button [navPush]="pushPage" [navParams]="params"></button>`
|
* template: `<button [navPush]="pushPage" [navParams]="params">Go</button>`
|
||||||
* })
|
* })
|
||||||
* class MyPage {
|
* class MyPage {
|
||||||
* constructor(){
|
* constructor(){
|
||||||
@ -32,61 +39,42 @@ import { NavController } from './nav-controller';
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* ### Alternate syntax
|
|
||||||
* You can also use syntax similar to Angular2's router, passing an array to
|
|
||||||
* NavPush:
|
|
||||||
* ```html
|
|
||||||
* <button [navPush]="[pushPage, params]"></button>
|
|
||||||
* ```
|
|
||||||
* @demo /docs/v2/demos/navigation/
|
* @demo /docs/v2/demos/navigation/
|
||||||
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
|
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
|
||||||
* @see {@link ../NavPop NavPop API Docs}
|
* @see {@link ../NavPop NavPop API Docs}
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[navPush]',
|
selector: '[navPush]'
|
||||||
host: {
|
|
||||||
'(click)': 'onClick()',
|
|
||||||
'role': 'link'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
export class NavPush {
|
export class NavPush {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {Page} the page you want to push
|
* @input {Page} The Page to push onto the Nav.
|
||||||
*/
|
|
||||||
@Input() navPush: any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @input {any} Any parameters you want to pass along
|
|
||||||
*/
|
|
||||||
@Input() navParams: any;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Optional() private _nav: NavController
|
|
||||||
) {
|
|
||||||
if (!_nav) {
|
|
||||||
console.error('nav-push must be within a NavController');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
onClick() {
|
@Input() navPush: any[]|string;
|
||||||
let destination: any, params: any;
|
|
||||||
|
|
||||||
if (this.navPush instanceof Array) {
|
/**
|
||||||
if (this.navPush.length > 2) {
|
* @input {any} Parameters to pass to the page.
|
||||||
throw 'Too many [navPush] arguments, expects [View, { params }]';
|
*/
|
||||||
}
|
@Input() navParams: {[k: string]: any};
|
||||||
destination = this.navPush[0];
|
|
||||||
params = this.navPush[1] || this.navParams;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
destination = this.navPush;
|
constructor(@Optional() private _nav: NavController) {
|
||||||
params = this.navParams;
|
if (!_nav) {
|
||||||
|
console.error('navPush must be within a NavController');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('click')
|
||||||
|
onClick(): boolean {
|
||||||
|
// If no target, or if target is _self, prevent default browser behavior
|
||||||
|
if (this._nav) {
|
||||||
|
this._nav.push(this.navPush, this.navParams, noop);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._nav && this._nav.push(destination, params);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { Config } from '../../config/config';
|
|||||||
import { Keyboard } from '../../util/keyboard';
|
import { Keyboard } from '../../util/keyboard';
|
||||||
import { GestureController } from '../../gestures/gesture-controller';
|
import { GestureController } from '../../gestures/gesture-controller';
|
||||||
import { isTrueProperty } from '../../util/util';
|
import { isTrueProperty } from '../../util/util';
|
||||||
import { NavController } from './nav-controller';
|
import { NavControllerBase } from './nav-controller-base';
|
||||||
import { ViewController } from './view-controller';
|
import { ViewController } from './view-controller';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,13 +114,13 @@ import { ViewController } from './view-controller';
|
|||||||
`,
|
`,
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
})
|
})
|
||||||
export class Nav extends NavController implements AfterViewInit {
|
export class Nav extends NavControllerBase implements AfterViewInit {
|
||||||
private _root: any;
|
private _root: any;
|
||||||
private _hasInit: boolean = false;
|
private _hasInit: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Optional() viewCtrl: ViewController,
|
@Optional() viewCtrl: ViewController,
|
||||||
@Optional() parent: NavController,
|
@Optional() parent: NavControllerBase,
|
||||||
app: App,
|
app: App,
|
||||||
config: Config,
|
config: Config,
|
||||||
keyboard: Keyboard,
|
keyboard: Keyboard,
|
||||||
@ -164,9 +164,6 @@ export class Nav extends NavController implements AfterViewInit {
|
|||||||
this._hasInit = true;
|
this._hasInit = true;
|
||||||
|
|
||||||
if (this._root) {
|
if (this._root) {
|
||||||
if (typeof this._root !== 'function') {
|
|
||||||
throw 'The [root] property in <ion-nav> must be given a reference to a component class from within the constructor.';
|
|
||||||
}
|
|
||||||
this.push(this._root);
|
this.push(this._root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { assign } from '../../util/util';
|
import { assign } from '../../util/util';
|
||||||
import { GestureController, GestureDelegate, GesturePriority } from '../../gestures/gesture-controller';
|
import { GestureController, GestureDelegate, GesturePriority } from '../../gestures/gesture-controller';
|
||||||
import { MenuController } from '../menu/menu-controller';
|
import { MenuController } from '../menu/menu-controller';
|
||||||
import { NavController } from './nav-controller';
|
import { NavControllerBase } from './nav-controller-base';
|
||||||
import { SlideData } from '../../gestures/slide-gesture';
|
import { SlideData } from '../../gestures/slide-gesture';
|
||||||
import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
|
import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ export class SwipeBackGesture extends SlideEdgeGesture {
|
|||||||
constructor(
|
constructor(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
options: any,
|
options: any,
|
||||||
private _nav: NavController,
|
private _nav: NavControllerBase,
|
||||||
gestureCtlr: GestureController
|
gestureCtlr: GestureController
|
||||||
) {
|
) {
|
||||||
super(element, assign({
|
super(element, assign({
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,11 @@
|
|||||||
import { ChangeDetectorRef, Component, ComponentResolver, ElementRef, EventEmitter, forwardRef, Input, Inject, NgZone, Output, Renderer, ViewChild, ViewEncapsulation, ViewContainerRef } from '@angular/core';
|
import { ChangeDetectorRef, Component, ComponentResolver, ElementRef, EventEmitter, forwardRef, Input, Inject, NgZone, Optional, Output, Renderer, ViewChild, ViewEncapsulation, ViewContainerRef } from '@angular/core';
|
||||||
|
|
||||||
import { App } from '../app/app';
|
import { App } from '../app/app';
|
||||||
import { Config } from '../../config/config';
|
import { Config } from '../../config/config';
|
||||||
import { GestureController } from '../../gestures/gesture-controller';
|
import { GestureController } from '../../gestures/gesture-controller';
|
||||||
import { isTrueProperty} from '../../util/util';
|
import { isTrueProperty} from '../../util/util';
|
||||||
import { Keyboard} from '../../util/keyboard';
|
import { Keyboard} from '../../util/keyboard';
|
||||||
import { NavController } from '../nav/nav-controller';
|
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||||
import { NavOptions} from '../nav/nav-interfaces';
|
import { NavOptions} from '../nav/nav-interfaces';
|
||||||
import { TabButton} from './tab-button';
|
import { TabButton} from './tab-button';
|
||||||
import { Tabs} from './tabs';
|
import { Tabs} from './tabs';
|
||||||
@ -128,7 +128,7 @@ import { ViewController} from '../nav/view-controller';
|
|||||||
template: '<div #viewport></div><div class="nav-decor"></div>',
|
template: '<div #viewport></div><div class="nav-decor"></div>',
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
})
|
})
|
||||||
export class Tab extends NavController {
|
export class Tab extends NavControllerBase {
|
||||||
private _isInitial: boolean;
|
private _isInitial: boolean;
|
||||||
private _isEnabled: boolean = true;
|
private _isEnabled: boolean = true;
|
||||||
private _isShown: boolean = true;
|
private _isShown: boolean = true;
|
||||||
@ -236,10 +236,6 @@ export class Tab extends NavController {
|
|||||||
|
|
||||||
parent.add(this);
|
parent.add(this);
|
||||||
|
|
||||||
if (parent.rootNav) {
|
|
||||||
this._sbEnabled = parent.rootNav.isSwipeBackEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._tabId = 'tabpanel-' + this.id;
|
this._tabId = 'tabpanel-' + this.id;
|
||||||
this._btnId = 'tab-' + this.id;
|
this._btnId = 'tab-' + this.id;
|
||||||
}
|
}
|
||||||
@ -264,7 +260,7 @@ export class Tab extends NavController {
|
|||||||
*/
|
*/
|
||||||
load(opts: NavOptions, done?: Function) {
|
load(opts: NavOptions, done?: Function) {
|
||||||
if (!this._loaded && this.root) {
|
if (!this._loaded && this.root) {
|
||||||
this.push(this.root, this.rootParams, opts).then(() => {
|
this.push(this.root, this.rootParams, opts, () => {
|
||||||
done(true);
|
done(true);
|
||||||
});
|
});
|
||||||
this._loaded = true;
|
this._loaded = true;
|
||||||
|
@ -7,9 +7,11 @@ import { Config } from '../../config/config';
|
|||||||
import { Content } from '../content/content';
|
import { Content } from '../content/content';
|
||||||
import { Icon } from '../icon/icon';
|
import { Icon } from '../icon/icon';
|
||||||
import { Ion } from '../ion';
|
import { Ion } from '../ion';
|
||||||
import { isBlank, isTrueProperty } from '../../util/util';
|
import { isBlank, isPresent, isTrueProperty } from '../../util/util';
|
||||||
import { nativeRaf } from '../../util/dom';
|
import { nativeRaf } from '../../util/dom';
|
||||||
import { NavController, DIRECTION_FORWARD } from '../nav/nav-controller';
|
import { NavController } from '../nav/nav-controller';
|
||||||
|
import { NavControllerBase } from '../nav/nav-controller-base';
|
||||||
|
import { NavOptions, DIRECTION_FORWARD } from '../nav/nav-interfaces';
|
||||||
import { Platform } from '../../platform/platform';
|
import { Platform } from '../../platform/platform';
|
||||||
import { Tab } from './tab';
|
import { Tab } from './tab';
|
||||||
import { TabButton } from './tab-button';
|
import { TabButton } from './tab-button';
|
||||||
@ -164,7 +166,7 @@ export class Tabs extends Ion {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
id: number;
|
id: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -219,7 +221,7 @@ export class Tabs extends Ion {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
parent: NavController;
|
parent: NavControllerBase;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Optional() parent: NavController,
|
@Optional() parent: NavController,
|
||||||
@ -232,8 +234,8 @@ export class Tabs extends Ion {
|
|||||||
) {
|
) {
|
||||||
super(_elementRef);
|
super(_elementRef);
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = <NavControllerBase>parent;
|
||||||
this.id = ++tabIds;
|
this.id = 't' + (++tabIds);
|
||||||
this._sbPadding = _config.getBoolean('statusbarPadding');
|
this._sbPadding = _config.getBoolean('statusbarPadding');
|
||||||
this._useHighlight = _config.getBoolean('tabsHighlight');
|
this._useHighlight = _config.getBoolean('tabsHighlight');
|
||||||
|
|
||||||
@ -248,9 +250,9 @@ export class Tabs extends Ion {
|
|||||||
this._useHighlight = _config.getBoolean('tabbarHighlight');
|
this._useHighlight = _config.getBoolean('tabbarHighlight');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent) {
|
if (this.parent) {
|
||||||
// this Tabs has a parent Nav
|
// this Tabs has a parent Nav
|
||||||
parent.registerChildNav(this);
|
this.parent.registerChildNav(this);
|
||||||
|
|
||||||
} else if (this._app) {
|
} else if (this._app) {
|
||||||
// this is the root navcontroller for the entire app
|
// this is the root navcontroller for the entire app
|
||||||
@ -320,41 +322,32 @@ export class Tabs extends Ion {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
initTabs() {
|
initTabs() {
|
||||||
// first check if preloadTab is set as an input @Input, then check the config
|
// get the selected index from the input
|
||||||
let preloadTabs = (isBlank(this.preloadTabs) ? this._config.getBoolean('preloadTabs') : isTrueProperty(this.preloadTabs));
|
// otherwise default it to use the first index
|
||||||
|
let selectedIndex = (isBlank(this.selectedIndex) ? 0 : parseInt(this.selectedIndex, 10));
|
||||||
|
|
||||||
// get the selected index
|
// get the selectedIndex and ensure it isn't hidden or disabled
|
||||||
let selectedIndex = this.selectedIndex ? parseInt(this.selectedIndex, 10) : 0;
|
let selectedTab = this._tabs.find((t, i) => i === selectedIndex && t.enabled && t.show);
|
||||||
|
if (!selectedTab) {
|
||||||
// ensure the selectedIndex isn't a hidden or disabled tab
|
// wasn't able to select the tab they wanted
|
||||||
// also find the first available index incase we need it later
|
// try to find the first tab that's available
|
||||||
let availableIndex = -1;
|
selectedTab = this._tabs.find(t => t.enabled && t.show);
|
||||||
this._tabs.forEach((tab, index) => {
|
|
||||||
if (tab.enabled && tab.show && availableIndex < 0) {
|
|
||||||
// we know this tab index is safe to show
|
|
||||||
availableIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index === selectedIndex && (!tab.enabled || !tab.show)) {
|
|
||||||
// the selectedIndex is not safe to show
|
|
||||||
selectedIndex = -1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (selectedIndex < 0) {
|
|
||||||
// the selected index wasn't safe to show
|
|
||||||
// instead use an available index found to be safe to show
|
|
||||||
selectedIndex = availableIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._tabs.forEach((tab, index) => {
|
if (selectedTab) {
|
||||||
if (index === selectedIndex) {
|
// we found a tab to select
|
||||||
this.select(tab);
|
this.select(selectedTab);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (preloadTabs) {
|
// check if preloadTab is set as an input @Input
|
||||||
tab.preload(1000 * index);
|
// otherwise check the preloadTabs config
|
||||||
}
|
let shouldPreloadTabs = (isBlank(this.preloadTabs) ? this._config.getBoolean('preloadTabs') : isTrueProperty(this.preloadTabs));
|
||||||
});
|
if (shouldPreloadTabs) {
|
||||||
|
// preload all the tabs which isn't the selected tab
|
||||||
|
this._tabs.filter((t) => t !== selectedTab).forEach((tab, index) => {
|
||||||
|
tab.preload(this._config.getNumber('tabsPreloadDelay', 1000) * index);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -379,31 +372,33 @@ export class Tabs extends Ion {
|
|||||||
/**
|
/**
|
||||||
* @param {number|Tab} tabOrIndex Index, or the Tab instance, of the tab to select.
|
* @param {number|Tab} tabOrIndex Index, or the Tab instance, of the tab to select.
|
||||||
*/
|
*/
|
||||||
select(tabOrIndex: number | Tab) {
|
select(tabOrIndex: number | Tab, opts: NavOptions = {}, done?: Function): Promise<any> {
|
||||||
|
let promise: Promise<any>;
|
||||||
|
if (!done) {
|
||||||
|
promise = new Promise(res => { done = res; });
|
||||||
|
}
|
||||||
|
|
||||||
let selectedTab: Tab = (typeof tabOrIndex === 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex);
|
let selectedTab: Tab = (typeof tabOrIndex === 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex);
|
||||||
if (isBlank(selectedTab)) {
|
if (isBlank(selectedTab)) {
|
||||||
return;
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
let deselectedTab = this.getSelected();
|
let deselectedTab = this.getSelected();
|
||||||
|
|
||||||
if (selectedTab === deselectedTab) {
|
if (selectedTab === deselectedTab) {
|
||||||
// no change
|
// no change
|
||||||
return this._touchActive(selectedTab);
|
this._touchActive(selectedTab);
|
||||||
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug(`Tabs, select: ${selectedTab.id}`);
|
console.debug(`Tabs, select: ${selectedTab.id}`);
|
||||||
|
|
||||||
let opts = {
|
|
||||||
animate: false
|
|
||||||
};
|
|
||||||
|
|
||||||
let deselectedPage: ViewController;
|
let deselectedPage: ViewController;
|
||||||
if (deselectedTab) {
|
if (deselectedTab) {
|
||||||
deselectedPage = deselectedTab.getActive();
|
deselectedPage = deselectedTab.getActive();
|
||||||
deselectedPage && deselectedPage.fireWillLeave();
|
deselectedPage && deselectedPage.fireWillLeave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts.animate = false;
|
||||||
|
|
||||||
let selectedPage = selectedTab.getActive();
|
let selectedPage = selectedTab.getActive();
|
||||||
selectedPage && selectedPage.fireWillEnter();
|
selectedPage && selectedPage.fireWillEnter();
|
||||||
|
|
||||||
@ -451,7 +446,11 @@ export class Tabs extends Ion {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -513,6 +512,13 @@ export class Tabs extends Ion {
|
|||||||
return this._tabs.indexOf(tab);
|
return this._tabs.indexOf(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
length(): number {
|
||||||
|
return this._tabs.length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* "Touch" the active tab, going back to the root view of the tab
|
* "Touch" the active tab, going back to the root view of the tab
|
||||||
@ -548,20 +554,6 @@ export class Tabs extends Ion {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Returns the root NavController. Returns `null` if Tabs is not
|
|
||||||
* within a NavController.
|
|
||||||
* @returns {NavController}
|
|
||||||
*/
|
|
||||||
get rootNav(): NavController {
|
|
||||||
let nav = this.parent;
|
|
||||||
while (nav && nav.parent) {
|
|
||||||
nav = nav.parent;
|
|
||||||
}
|
|
||||||
return nav;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* DOM WRITE
|
* DOM WRITE
|
||||||
|
@ -1,10 +1,89 @@
|
|||||||
import {Component} from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import {App, Nav, Tabs, Tab, NavOptions, Config, ViewController, Platform} from '../../../../src';
|
import { App, Config, Nav, NavOptions, Platform, Tab, Tabs, ViewController } from '../../../../src';
|
||||||
|
import { mockTab, mockTabs } from '../../../../src/util/mock-providers';
|
||||||
|
|
||||||
export function run() {
|
export function run() {
|
||||||
|
|
||||||
describe('Tabs', () => {
|
describe('Tabs', () => {
|
||||||
|
|
||||||
|
describe('initTabs', () => {
|
||||||
|
|
||||||
|
it('should preload all tabs', () => {
|
||||||
|
var tabs = mockTabs();
|
||||||
|
var tab0 = mockTab(tabs);
|
||||||
|
var tab1 = mockTab(tabs);
|
||||||
|
tab0.root = SomePage;
|
||||||
|
tab1.root = SomePage;
|
||||||
|
|
||||||
|
tab0.preload = () => {};
|
||||||
|
tab1.preload = () => {};
|
||||||
|
|
||||||
|
spyOn(tab0, 'preload');
|
||||||
|
spyOn(tab1, 'preload');
|
||||||
|
|
||||||
|
tabs.preloadTabs = true;
|
||||||
|
|
||||||
|
tabs.initTabs();
|
||||||
|
|
||||||
|
expect(tab0.isSelected).toEqual(true);
|
||||||
|
expect(tab1.isSelected).toEqual(false);
|
||||||
|
|
||||||
|
expect(tab0.preload).not.toHaveBeenCalled();
|
||||||
|
expect(tab1.preload).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not select a hidden or disabled tab', () => {
|
||||||
|
var tabs = mockTabs();
|
||||||
|
var tab0 = mockTab(tabs);
|
||||||
|
var tab1 = mockTab(tabs);
|
||||||
|
tab0.root = SomePage;
|
||||||
|
tab1.root = SomePage;
|
||||||
|
|
||||||
|
tab1.enabled = false;
|
||||||
|
tab1.show = false;
|
||||||
|
|
||||||
|
tabs.selectedIndex = '1';
|
||||||
|
tabs.initTabs();
|
||||||
|
|
||||||
|
expect(tab0.isSelected).toEqual(true);
|
||||||
|
expect(tab1.isSelected).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select the second tab from selectedIndex input', () => {
|
||||||
|
var tabs = mockTabs();
|
||||||
|
var tab0 = mockTab(tabs);
|
||||||
|
var tab1 = mockTab(tabs);
|
||||||
|
tab0.root = SomePage;
|
||||||
|
tab1.root = SomePage;
|
||||||
|
|
||||||
|
tabs.selectedIndex = '1';
|
||||||
|
tabs.initTabs();
|
||||||
|
|
||||||
|
expect(tab0.isSelected).toEqual(false);
|
||||||
|
expect(tab1.isSelected).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select the first tab by default', () => {
|
||||||
|
var tabs = mockTabs();
|
||||||
|
var tab0 = mockTab(tabs);
|
||||||
|
var tab1 = mockTab(tabs);
|
||||||
|
tab0.root = SomePage;
|
||||||
|
tab1.root = SomePage;
|
||||||
|
|
||||||
|
spyOn(tab0, 'preload');
|
||||||
|
spyOn(tab1, 'preload');
|
||||||
|
|
||||||
|
tabs.initTabs();
|
||||||
|
|
||||||
|
expect(tab0.isSelected).toEqual(true);
|
||||||
|
expect(tab1.isSelected).toEqual(false);
|
||||||
|
|
||||||
|
expect(tab0.preload).not.toHaveBeenCalled();
|
||||||
|
expect(tab1.preload).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('previousTab', () => {
|
describe('previousTab', () => {
|
||||||
|
|
||||||
it('should find the previous tab when there has been 3 selections', () => {
|
it('should find the previous tab when there has been 3 selections', () => {
|
||||||
@ -12,9 +91,6 @@ describe('Tabs', () => {
|
|||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
var tab2 = mockTab(tabs);
|
var tab2 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
tabs.add(tab2);
|
|
||||||
tab0.root = SomePage;
|
tab0.root = SomePage;
|
||||||
tab1.root = SomePage;
|
tab1.root = SomePage;
|
||||||
tab2.root = SomePage;
|
tab2.root = SomePage;
|
||||||
@ -36,8 +112,6 @@ describe('Tabs', () => {
|
|||||||
var tabs = mockTabs();
|
var tabs = mockTabs();
|
||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
tab0.root = SomePage;
|
tab0.root = SomePage;
|
||||||
tab1.root = SomePage;
|
tab1.root = SomePage;
|
||||||
|
|
||||||
@ -56,8 +130,6 @@ describe('Tabs', () => {
|
|||||||
var tabs = mockTabs();
|
var tabs = mockTabs();
|
||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
tab0.root = SomePage;
|
tab0.root = SomePage;
|
||||||
tab1.root = SomePage;
|
tab1.root = SomePage;
|
||||||
|
|
||||||
@ -87,8 +159,6 @@ describe('Tabs', () => {
|
|||||||
var tabs = mockTabs();
|
var tabs = mockTabs();
|
||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
|
|
||||||
tab0.root = SomePage;
|
tab0.root = SomePage;
|
||||||
tab1.root = SomePage;
|
tab1.root = SomePage;
|
||||||
@ -103,12 +173,11 @@ describe('Tabs', () => {
|
|||||||
var tabs = mockTabs();
|
var tabs = mockTabs();
|
||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
|
|
||||||
tab0.root = SomePage;
|
tab0.root = SomePage;
|
||||||
tab1.root = SomePage;
|
tab1.root = SomePage;
|
||||||
|
|
||||||
|
expect(tabs.length()).toEqual(2);
|
||||||
expect(tab0.isSelected).toBeUndefined();
|
expect(tab0.isSelected).toBeUndefined();
|
||||||
expect(tab1.isSelected).toBeUndefined();
|
expect(tab1.isSelected).toBeUndefined();
|
||||||
|
|
||||||
@ -118,16 +187,6 @@ describe('Tabs', () => {
|
|||||||
expect(tab1.isSelected).toEqual(false);
|
expect(tab1.isSelected).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not select an invalid tab index', () => {
|
|
||||||
var tabs = mockTabs();
|
|
||||||
var tab0 = mockTab(tabs);
|
|
||||||
var tab1 = mockTab(tabs);
|
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
|
|
||||||
expect(tabs.select(22)).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getByIndex', () => {
|
describe('getByIndex', () => {
|
||||||
@ -137,8 +196,6 @@ describe('Tabs', () => {
|
|||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
tab0.setRoot(<any>{});
|
tab0.setRoot(<any>{});
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
|
|
||||||
expect(tabs.getIndex(tab0)).toEqual(0);
|
expect(tabs.getIndex(tab0)).toEqual(0);
|
||||||
expect(tabs.getIndex(tab1)).toEqual(1);
|
expect(tabs.getIndex(tab1)).toEqual(1);
|
||||||
@ -152,8 +209,6 @@ describe('Tabs', () => {
|
|||||||
var tabs = mockTabs();
|
var tabs = mockTabs();
|
||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
|
|
||||||
tab1.setSelected(true);
|
tab1.setSelected(true);
|
||||||
|
|
||||||
@ -164,48 +219,15 @@ describe('Tabs', () => {
|
|||||||
var tabs = mockTabs();
|
var tabs = mockTabs();
|
||||||
var tab0 = mockTab(tabs);
|
var tab0 = mockTab(tabs);
|
||||||
var tab1 = mockTab(tabs);
|
var tab1 = mockTab(tabs);
|
||||||
tabs.add(tab0);
|
|
||||||
tabs.add(tab1);
|
|
||||||
|
|
||||||
expect(tabs.getSelected()).toEqual(null);
|
expect(tabs.getSelected()).toEqual(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var app: App;
|
|
||||||
var config: Config;
|
|
||||||
var platform: Platform;
|
|
||||||
var _cd: any;
|
|
||||||
|
|
||||||
function mockNav(): Nav {
|
|
||||||
return new Nav(null, null, null, config, null, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mockTabs(): Tabs {
|
|
||||||
return new Tabs(null, null, null, config, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mockTab(parentTabs: Tabs): Tab {
|
|
||||||
var tab = new Tab(parentTabs, app, config, null, null, null, null, null, _cd);
|
|
||||||
tab.load = function(opts: any, cb: Function) {
|
|
||||||
cb();
|
|
||||||
};
|
|
||||||
return tab;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({})
|
@Component({})
|
||||||
class SomePage {}
|
class SomePage {}
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
config = new Config();
|
|
||||||
platform = new Platform();
|
|
||||||
app = new App(config, platform);
|
|
||||||
_cd = {
|
|
||||||
reattach: function(){},
|
|
||||||
detach: function(){}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
171
src/util/mock-providers.ts
Normal file
171
src/util/mock-providers.ts
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import { ChangeDetectorRef, ElementRef, NgZone, Renderer } from '@angular/core';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
|
import { App, Config, Form, GestureController, Keyboard, MenuController, NavOptions, Platform, Tab, Tabs, Transition, ViewController } from '../../src';
|
||||||
|
import { NavControllerBase } from '../../src/components/nav/nav-controller-base';
|
||||||
|
|
||||||
|
|
||||||
|
export const mockConfig = function(config?: any) {
|
||||||
|
return new Config(config);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockPlatform = function(platforms?: string[]) {
|
||||||
|
return new Platform(platforms);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockApp = function(config?: Config, platform?: Platform) {
|
||||||
|
config = config || mockConfig();
|
||||||
|
platform = platform || mockPlatform();
|
||||||
|
return new App(config, platform);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockZone = function(): NgZone {
|
||||||
|
let zone: any = {
|
||||||
|
run: function(cb: any) {
|
||||||
|
cb();
|
||||||
|
},
|
||||||
|
runOutsideAngular: function(cb: any) {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return zone;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockChangeDetectorRef = function(): ChangeDetectorRef {
|
||||||
|
let cd: any = {
|
||||||
|
reattach: () => {},
|
||||||
|
detach: () => {}
|
||||||
|
};
|
||||||
|
return cd;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockElementRef = function(): ElementRef {
|
||||||
|
return {
|
||||||
|
nativeElement: document.createElement('div')
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockRenderer = function(): Renderer {
|
||||||
|
let renderer: any = {
|
||||||
|
setElementAttribute: () => {},
|
||||||
|
setElementClass: () => {},
|
||||||
|
setElementStyle: () => {}
|
||||||
|
};
|
||||||
|
return renderer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockLocation = function(): Location {
|
||||||
|
let location: any = {
|
||||||
|
path: () => { return ''; },
|
||||||
|
subscribe: () => {},
|
||||||
|
go: () => {},
|
||||||
|
back: () => {}
|
||||||
|
};
|
||||||
|
return location;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockTransition = function(playCallback: Function, duration: number) {
|
||||||
|
return function _createTrans(enteringView: ViewController, leavingView: ViewController, transitionOpts: any): Transition {
|
||||||
|
let transition: any = {
|
||||||
|
play: () => {
|
||||||
|
playCallback();
|
||||||
|
},
|
||||||
|
getDuration: () => { return duration; },
|
||||||
|
onFinish: () => {}
|
||||||
|
};
|
||||||
|
return transition;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockNavController = function(): NavControllerBase {
|
||||||
|
let platform = mockPlatform();
|
||||||
|
|
||||||
|
let config = mockConfig();
|
||||||
|
config.setPlatform(platform);
|
||||||
|
|
||||||
|
let app = mockApp(config, platform);
|
||||||
|
|
||||||
|
let form = new Form();
|
||||||
|
|
||||||
|
let zone = mockZone();
|
||||||
|
|
||||||
|
let keyboard = new Keyboard(config, form, zone);
|
||||||
|
|
||||||
|
let elementRef = mockElementRef();
|
||||||
|
|
||||||
|
let renderer = mockRenderer();
|
||||||
|
|
||||||
|
let compiler: any = null;
|
||||||
|
|
||||||
|
let gestureCtrl = new GestureController(app);
|
||||||
|
|
||||||
|
let location = mockLocation();
|
||||||
|
|
||||||
|
return new NavControllerBase(
|
||||||
|
null,
|
||||||
|
app,
|
||||||
|
config,
|
||||||
|
keyboard,
|
||||||
|
elementRef,
|
||||||
|
zone,
|
||||||
|
renderer,
|
||||||
|
compiler,
|
||||||
|
gestureCtrl
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockTab = function(parentTabs: Tabs): Tab {
|
||||||
|
let platform = mockPlatform();
|
||||||
|
|
||||||
|
let config = mockConfig();
|
||||||
|
config.setPlatform(platform);
|
||||||
|
|
||||||
|
let app = (<any>parentTabs)._app || mockApp(config, platform);
|
||||||
|
|
||||||
|
let form = new Form();
|
||||||
|
|
||||||
|
let zone = mockZone();
|
||||||
|
|
||||||
|
let keyboard = new Keyboard(config, form, zone);
|
||||||
|
|
||||||
|
let elementRef = mockElementRef();
|
||||||
|
|
||||||
|
let renderer = mockRenderer();
|
||||||
|
|
||||||
|
let changeDetectorRef = mockChangeDetectorRef();
|
||||||
|
|
||||||
|
let compiler: any = null;
|
||||||
|
|
||||||
|
let gestureCtrl = new GestureController(app);
|
||||||
|
|
||||||
|
let location = mockLocation();
|
||||||
|
|
||||||
|
let tab = new Tab(
|
||||||
|
parentTabs,
|
||||||
|
app,
|
||||||
|
config,
|
||||||
|
keyboard,
|
||||||
|
elementRef,
|
||||||
|
zone,
|
||||||
|
renderer,
|
||||||
|
compiler,
|
||||||
|
changeDetectorRef,
|
||||||
|
gestureCtrl
|
||||||
|
);
|
||||||
|
|
||||||
|
tab.load = (opts: any, cb: Function) => {
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
return tab;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mockTabs = function(app?: App): Tabs {
|
||||||
|
let config = mockConfig();
|
||||||
|
let platform = mockPlatform();
|
||||||
|
app = app || mockApp(config, platform);
|
||||||
|
let elementRef = mockElementRef();
|
||||||
|
let renderer = mockRenderer();
|
||||||
|
|
||||||
|
return new Tabs(null, null, app, config, elementRef, platform, renderer);
|
||||||
|
};
|
@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
export function noop() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a min and max, restrict the given number
|
* Given a min and max, restrict the given number
|
||||||
* to the range.
|
* to the range.
|
||||||
|
Reference in New Issue
Block a user