Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tests/app/app/main-page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Button {
color: green;
}
28 changes: 27 additions & 1 deletion tests/app/livesync/livesync-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const mainPageCssFileName = "./app/main-page.css";
const mainPageHtmlFileName = "./app/main-page.html";
const mainPageXmlFileName = "./app/main-page.xml";

const black = new Color("black");
const green = new Color("green");

const mainPageTemplate = `
Expand Down Expand Up @@ -56,7 +57,7 @@ export function test_onLiveSync_ModuleContext_Script_AppTs() {
}

export function test_onLiveSync_ModuleContext_Style_MainPageCss() {
_test_onLiveSync_ModuleContext({ type: "style", path: mainPageCssFileName });
_test_onLiveSync_ModuleContext_TypeStyle({ type: "style", path: mainPageCssFileName });
}

export function test_onLiveSync_ModuleContext_Markup_MainPageHtml() {
Expand Down Expand Up @@ -110,4 +111,29 @@ function _test_onLiveSync_ModuleContext(context: { type, path }) {
const topmostFrame = frame.topmost();
TKUnit.waitUntilReady(() => topmostFrame.currentPage && topmostFrame.currentPage.isLoaded && !topmostFrame.canGoBack());
TKUnit.assertTrue(topmostFrame.currentPage.getViewById("label").isLoaded);
}

function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) {
const pageBeforeNavigation = helper.getCurrentPage();

const page = <Page>parse(pageTemplate);
helper.navigateWithHistory(() => page);

const pageBeforeLiveSync = helper.getCurrentPage();
pageBeforeLiveSync._moduleName = "main-page";
global.__onLiveSync({ type: context.type, path: context.path });

const pageAfterLiveSync = helper.getCurrentPage();
TKUnit.waitUntilReady(() => pageAfterLiveSync.getViewById("button").style.color.toString() === green.toString());

TKUnit.assertTrue(pageAfterLiveSync.frame.canGoBack(), "Local styles NOT applied - livesync navigation executed!");
TKUnit.assertEqual(pageAfterLiveSync, pageBeforeLiveSync, "Pages are different - livesync navigation executed!");
TKUnit.assertTrue(pageAfterLiveSync._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version NOT applied!");

helper.goBack();

const pageAfterNavigationBack = helper.getCurrentPage();
TKUnit.assertEqual(pageAfterNavigationBack.getViewById("label").style.color, black, "App styles applied on back navigation!");
TKUnit.assertEqual(pageBeforeNavigation, pageAfterNavigationBack, "Pages are different - livesync navigation executed!");
TKUnit.assertTrue(pageAfterNavigationBack._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
}
9 changes: 6 additions & 3 deletions tests/app/testRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,15 @@ allTests["VISUAL-STATE"] = visualStateTests;
import * as valueSourceTests from "./ui/styling/value-source-tests";
allTests["VALUE-SOURCE"] = valueSourceTests;

import * as buttonTests from "./ui/button/button-tests";
allTests["BUTTON"] = buttonTests;

import * as borderTests from "./ui/border/border-tests";
allTests["BORDER"] = borderTests;

import * as builderTests from "./ui/builder/builder-tests";
allTests["BUILDER"] = builderTests;

import * as buttonTests from "./ui/button/button-tests";
Comment thread
manoldonev marked this conversation as resolved.
Resolved
allTests["BUTTON"] = buttonTests;

import * as labelTests from "./ui/label/label-tests";
allTests["LABEL"] = labelTests;

Expand Down
26 changes: 26 additions & 0 deletions tests/app/ui/builder/builder-tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { path } from "tns-core-modules/file-system";
import { loadPage } from "tns-core-modules/ui/builder";
import { assertEqual, assertNull } from "../../TKUnit";

const COMPONENT_MODULE = "component-module";
const LABEL = "label";

function getViewComponent() {
const moduleNamePath = path.join(__dirname, COMPONENT_MODULE);
const fileName = path.join(__dirname, `${COMPONENT_MODULE}.xml`);
const view = loadPage(moduleNamePath, fileName);
return view;
}

export function test_view_is_module_root_component() {
const view = getViewComponent();
const actualModule = view._moduleName;
assertEqual(actualModule, COMPONENT_MODULE, `View<${view}> is NOT root component of module <${COMPONENT_MODULE}>.`);
}

export function test_view_is_NOT_module_root_component() {
const view = getViewComponent();
const nestedView = view.getViewById(`${LABEL}`);
const undefinedModule = nestedView._moduleName;
assertNull(undefinedModule, `View<${nestedView}> should NOT be a root component of a module.`);
}
3 changes: 3 additions & 0 deletions tests/app/ui/builder/component-module.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<StackLayout>
<Label id="label"></Label>
</StackLayout>
16 changes: 12 additions & 4 deletions tns-core-modules/ui/builder/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ export function load(pathOrOptions: string | LoadOptions, context?: any): View {

export function loadPage(moduleNamePath: string, fileName: string, context?: any): View {
const componentModule = loadInternal(fileName, context, moduleNamePath);
return componentModule && componentModule.component;
const componentView = componentModule && componentModule.component;
markAsModuleRoot(componentView, moduleNamePath);
return componentView;
}

const loadModule = profile("loadModule", (moduleNamePath: string, entry: ViewEntry): ModuleExports => {
Expand Down Expand Up @@ -96,7 +98,7 @@ export const createViewFromEntry = profile("createViewFromEntry", (entry: ViewEn
} else if (entry.moduleName) {
// Current app full path.
const currentAppPath = knownFolders.currentApp().path;

// Full path of the module = current app full path + module name.
const moduleNamePath = path.join(currentAppPath, entry.moduleName);
const moduleExports = loadModule(moduleNamePath, entry);
Expand All @@ -108,7 +110,7 @@ export const createViewFromEntry = profile("createViewFromEntry", (entry: ViewEn
return viewFromBuilder(moduleNamePath, moduleExports);
}
}

throw new Error("Failed to load page XML file for module: " + entry.moduleName);
});

Expand All @@ -128,14 +130,20 @@ interface ModuleExports {
const moduleCreateView = profile("module.createView", (moduleNamePath: string, moduleExports: ModuleExports): View => {
const view = moduleExports.createPage();
const cssFileName = resolveFileName(moduleNamePath, "css");

// If there is no cssFile only appCss will be applied at loaded.
if (cssFileName) {
view.addCssFile(cssFileName);
}
return view;
});

function markAsModuleRoot(componentView: View, moduleNamePath: string): void {
const lastIndexOfSeparator = moduleNamePath.lastIndexOf(path.separator);
const moduleName = moduleNamePath.substring(lastIndexOfSeparator + 1);
componentView._moduleName = moduleName;
}

function loadInternal(fileName: string, context?: any, moduleNamePath?: string): ComponentModule {
let componentModule: ComponentModule;

Expand Down
6 changes: 6 additions & 0 deletions tns-core-modules/ui/core/view-base/view-base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ export abstract class ViewBase extends Observable {
flexWrapBefore: FlexWrapBefore;
alignSelf: AlignSelf;

/**
* @private
* Module name when the view is a module root. Otherwise, it is undefined.
*/
_moduleName?: string;

//@private
/**
* @private
Expand Down
6 changes: 4 additions & 2 deletions tns-core-modules/ui/core/view-base/view-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
public _defaultPaddingLeft: number;
public _isPaddingRelative: boolean;

public _moduleName: string;

constructor() {
super();
this._domId = viewIdCounter++;
Expand Down Expand Up @@ -651,7 +653,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
}

public resetNativeView(): void {
//
//
}

private resetNativeViewInternal(): void {
Expand Down Expand Up @@ -688,7 +690,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition

this._context = context;

// This will account for nativeView that is created in createNativeView, recycled
// This will account for nativeView that is created in createNativeView, recycled
// or for backward compatability - set before _setupUI in iOS contructor.
let nativeView = this.nativeViewProtected;

Expand Down
39 changes: 32 additions & 7 deletions tns-core-modules/ui/core/view/view-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from ".";

import {
ViewBase, Property, booleanConverter, EventData, layout,
ViewBase, Property, booleanConverter, eachDescendant, EventData, layout,
getEventOrGestureName, traceEnabled, traceWrite, traceCategories,
InheritedProperty,
ShowModalOptions
Expand Down Expand Up @@ -136,6 +136,37 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
}
}

public _onLivesync(context?: ModuleContext): boolean {
_rootModalViews.forEach(v => v.closeModal());
_rootModalViews.length = 0;

// Currently, we pass `context` only for style modules
if (context && context.path) {
return this.changeLocalStyles(context.path);
}

return false;
}

private changeLocalStyles(contextPath: string): boolean {
if (!this.changeStyles(this, contextPath)) {
eachDescendant(this, (child: ViewBase) => {
this.changeStyles(child, contextPath);
return true;
});
}
// Do not execute frame navigation for a change in styles
return true;
}

private changeStyles(view: ViewBase, contextPath: string): boolean {
if (view._moduleName && contextPath.includes(view._moduleName)) {
(<this>view).changeCssFile(contextPath);
return true;
}
return false;
}

_setupAsRootView(context: any): void {
super._setupAsRootView(context);
if (!this._styleScope) {
Expand Down Expand Up @@ -210,12 +241,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
}
}

_onLivesync(): boolean {
_rootModalViews.forEach(v => v.closeModal());
_rootModalViews.length = 0;
return false;
}

public onBackPressed(): boolean {
return false;
}
Expand Down
56 changes: 23 additions & 33 deletions tns-core-modules/ui/frame/frame-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,44 +564,34 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
}

public _onLivesync(context?: ModuleContext): boolean {
super._onLivesync();

if (!this._currentEntry || !this._currentEntry.entry) {
return false;
}

const currentEntry = this._currentEntry.entry;
if (context && context.path) {
// Use topmost instead of this to cover nested frames scenario
const topmostFrame = topmost();
const moduleName = topmostFrame.currentEntry.moduleName;
const reapplyStyles = context.path.includes(moduleName);
if (reapplyStyles && moduleName) {
topmostFrame.currentPage.changeCssFile(context.path);
return true;
// Execute a navigation if not handled on `View` level
if (!super._onLivesync(context)) {
if (!this._currentEntry || !this._currentEntry.entry) {
return false;
}
}

const newEntry: NavigationEntry = {
animated: false,
clearHistory: true,
context: currentEntry.context,
create: currentEntry.create,
moduleName: currentEntry.moduleName,
backstackVisible: currentEntry.backstackVisible
}
const currentEntry = this._currentEntry.entry;
const newEntry: NavigationEntry = {
animated: false,
clearHistory: true,
context: currentEntry.context,
create: currentEntry.create,
moduleName: currentEntry.moduleName,
backstackVisible: currentEntry.backstackVisible
}

// If create returns the same page instance we can't recreate it.
// Instead of navigation set activity content.
// This could happen if current page was set in XML as a Page instance.
if (newEntry.create) {
const page = newEntry.create();
if (page === this.currentPage) {
return false;
// If create returns the same page instance we can't recreate it.
// Instead of navigation set activity content.
// This could happen if current page was set in XML as a Page instance.
if (newEntry.create) {
const page = newEntry.create();
if (page === this.currentPage) {
return false;
}
}
}

this.navigate(newEntry);
this.navigate(newEntry);
}
return true;
}
}
Expand Down