diff --git a/CHANGELOG.md b/CHANGELOG.md
index c29a3042a..8a3ad5f42 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,26 @@
+
+# [5.1.0](https://github.com/NativeScript/NativeScript/compare/5.0.5...5.1.0) (2018-12-05)
+
+
+### Bug Fixes
+
+* **android-styling:** correctly detect if drawable uses ColorFilter ([#6342](https://github.com/NativeScript/NativeScript/issues/6342)) ([11d3884](https://github.com/NativeScript/NativeScript/commit/11d3884)), closes [#6341](https://github.com/NativeScript/NativeScript/issues/6341)
+* **listview:** incorrect layout when scroll ([#6656](https://github.com/NativeScript/NativeScript/issues/6656)) ([63be78a](https://github.com/NativeScript/NativeScript/commit/63be78a))
+* **modals:** regression with modals in angular ([#6655](https://github.com/NativeScript/NativeScript/issues/6655)) ([30df4d9](https://github.com/NativeScript/NativeScript/commit/30df4d9))
+* **searchbar:** isEnabled and isUserInteractionEnabled ([#6636](https://github.com/NativeScript/NativeScript/issues/6636)) ([25c99d8](https://github.com/NativeScript/NativeScript/commit/25c99d8))
+* **tests:** Sporadic failures after upgrade to WebKit 12.0 ([#6635](https://github.com/NativeScript/NativeScript/issues/6635)) ([65e3a19](https://github.com/NativeScript/NativeScript/commit/65e3a19))
+
+
+### Features
+
+* add hints options for username and password fields ([#6416](https://github.com/NativeScript/NativeScript/issues/6416)) ([c834181](https://github.com/NativeScript/NativeScript/commit/c834181))
+* **ActionBar:** apply text color when largeTitletextAttribute is set on iOS ([#6631](https://github.com/NativeScript/NativeScript/issues/6631)) ([b58dff0](https://github.com/NativeScript/NativeScript/commit/b58dff0))
+* **modals:** Enable modal dialog chaining in IOS ([#6637](https://github.com/NativeScript/NativeScript/issues/6637)) ([64bccb9](https://github.com/NativeScript/NativeScript/commit/64bccb9))
+* **scrollbar:** add isScrollEnabled property ([#6640](https://github.com/NativeScript/NativeScript/issues/6640)) ([ae0fa90](https://github.com/NativeScript/NativeScript/commit/ae0fa90))
+* **tabview:** add androidSwipeEnabled property ([#6652](https://github.com/NativeScript/NativeScript/issues/6652)) ([0b239d7](https://github.com/NativeScript/NativeScript/commit/0b239d7))
+
+
+
## [5.0.5](https://github.com/NativeScript/NativeScript/compare/5.0.4...5.0.5) (2018-11-26)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2a4e2f32e..6a32b87fa 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -177,7 +177,7 @@ module (usually named AppModule).
ApplicationModule.
All other NgModules in the app (both feature and lazy-loaded ones)
should import the NativeScriptCommonModule instead.
-The behaviour is alligned with BrowserModule and CommonModule in web
+The behavior is aligned with BrowserModule and CommonModule in web
Angular apps. angular.io/guide/ngmodule-faq#q-browser-vs-common-module
Migration steps:
In all NgModules, instead of the root one, replace:
@@ -211,43 +211,98 @@ If you want to contribute, but you are not sure where to start - look for [issue
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
-## Releasing new versions
+## Releasing new versions
Instructions how to release a new version for **NativeScript Core Team Members**.
-1. Execute `npm install` to install dependencies:
-```bash
-npm install
-```
+
-2. Add the following to your `.npmrc`:
-```
-tag-version-prefix=""
-message="release: cut the %s release"
-```
+### Here are the steps described in the diagram above.
-3. Create new branch based on `master`:
-```bash
-git checkout -b username/release-version
+1. Checkout release branch
+```
+git checkout release
+```
+2. Create a PR to cut the release:
+```
+git checkout -b release-version
+git push --set-upstream origin release-version
+```
+#### Merge master in release branch or cherry-pick commits. If the commits are in release branch **skip this step**.
+```
+git merge --ff-only origin/master or git cherry-pick commit-sha
+git push --set-upstream origin prep-release-version
+```
+3. Execute `npm i` to install dependencies:
+```
+npm i
```
-
4. Execute [`npm version`](https://docs.npmjs.com/cli/version) to bump the version of `tns-platform-declarations`:
-```bash
+```
cd tns-platform-declarations
-npm --no-git-tag-version version [patch|minor|major]
+npm --no-git-tag-version version [major|minor|patch] -m "release: cut the %s release"
cd ..
```
-5. Execute [`npm version`](https://docs.npmjs.com/cli/version) to bump the version of `tns-core-modules`, tag the release and update the CHANGELOG.md:
-```bash
+5. Execute [`npm version`](https://docs.npmjs.com/cli/version) to bump the version of `tns-core-modules`,
+tag the release and update the CHANGELOG.md.
+In case we need to publish release version we need simply to use npm version x.x.x-rc
+```
cd tns-core-modules
-npm version [patch|minor|major]
+npm --no-git-tag-version version [major|minor|patch] -m "release: cut the %s release"
cd ..
```
+6. Set correct version of **tns-core-modules-widgets** in tns-core-modules/package.json.
+Usually tns-core-modules-widgets should already have been released and we need to set the official version.
-6. Push all the changes to your branch and open a pull request:
-```bash
-git push --set-upstream origin username/release-version --tags
+7. Add changes
+```
+git add changed-files
+git commit -m "release: cut the %s release"
+git push
+```
+8. Create git tag
+```
+git tag release-version
+git push --tags
+```
+9. Merge PR into release branch.
+
+10. If all checks has passed publish package.
+
+## Merge changes from release into master
+
+
+
+### Here are steps described in the diagram above.
+
+1. Make sure you are in release branch:
+```
+git checkout release
+git pull
+```
+2. Create PR to merge changes back in master and preserve history:
+```
+git checkout -b merge-release-in-master
+git push --set-upstream origin merge-release-in-master
+git merge origin/master
+```
+3. Resolve conflicts. Choose to keep the version of master branch. If it is needed to revert versions of modules, see at the bottom.
+
+4. Add conflicts:
+```
+git add resolved files
+```
+5. Commit changes with default merge message:
+```
+git commit
+git push
```
-7. Create `release` branch after the pull request is merged to `master` and publish the packages built on CI to `npm`.
+**If needed, revert version of modules and platform declarations to take the one from master:**
+```
+git checkout origin/master tns-platform-declarations/package.json tns-core-modules/package.json
+git commit --amend
+git push --force-with-lease
+```
+This will require to repeat steps from 1 to 4, since we need to keep the branches with the same history
diff --git a/LICENSE b/LICENSE
index 59dc28ed0..9d63a0a67 100755
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright (c) 2015-2018 Telerik EAD
+ Copyright (c) 2015-2018 Progress Software Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/apps/app/ui-tests-app/issues/issue-ng-repo-1626.ts b/apps/app/ui-tests-app/issues/issue-ng-repo-1626.ts
new file mode 100644
index 000000000..f52d419ff
--- /dev/null
+++ b/apps/app/ui-tests-app/issues/issue-ng-repo-1626.ts
@@ -0,0 +1,31 @@
+import { fromObject } from "tns-core-modules/data/observable";
+
+export function onLoaded(args) {
+ const page = args.object;
+ page.bindingContext = fromObject({
+ items: [
+ { id: 1, name: "Ter Stegen", role: "Goalkeeper" },
+ { id: 3, name: "Piqué", role: "Defender" },
+ { id: 4, name: "I. Rakitic", role: "Midfielder" },
+ { id: 5, name: "Sergio", role: "Midfielder" },
+ { id: 6, name: "Denis Suárez", role: "Midfielder\nSecond line for the sake of testing" },
+ { id: 7, name: "Arda", role: "Midfielder" },
+ { id: 8, name: "A. Iniesta", role: "Midfielder" },
+ { id: 9, name: "Suárez", role: "Forward" },
+ { id: 10, name: "Messi", role: "Forward" },
+ { id: 11, name: "Neymar", role: "Forward\nSecond line for the sake of testing" },
+ { id: 12, name: "Rafinha", role: "Midfielder\nSecond line for the sake of testing" },
+ { id: 13, name: "Cillessen", role: "Goalkeeper\nSecond line for the sake of testing" },
+ { id: 14, name: "Mascherano", role: "Defender" },
+ { id: 17, name: "Paco Alcácer", role: "Forward" },
+ { id: 18, name: "Jordi Alba", role: "Defender\nSecond line for the sake of testing" },
+ { id: 19, name: "Digne", role: "Defender" },
+ { id: 20, name: "Sergi Roberto", role: "Midfielder\nSecond line for the sake of testing" },
+ { id: 21, name: "André Gomes", role: "Midfielder\nSecond line for the sake of testing" },
+ { id: 22, name: "Aleix Vidal", role: "Midfielder" },
+ { id: 23, name: "Umtiti", role: "Defender" },
+ { id: 24, name: "Mathieu", role: "Defender" },
+ { id: 25, name: "Masip", role: "Goalkeeper" },
+ ]
+ });
+}
\ No newline at end of file
diff --git a/apps/app/ui-tests-app/issues/issue-ng-repo-1626.xml b/apps/app/ui-tests-app/issues/issue-ng-repo-1626.xml
new file mode 100644
index 000000000..b681a2c02
--- /dev/null
+++ b/apps/app/ui-tests-app/issues/issue-ng-repo-1626.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/app/ui-tests-app/issues/main-page.ts b/apps/app/ui-tests-app/issues/main-page.ts
index 82f8ee5fc..349b9634c 100644
--- a/apps/app/ui-tests-app/issues/main-page.ts
+++ b/apps/app/ui-tests-app/issues/main-page.ts
@@ -28,6 +28,7 @@ export function loadExamples() {
examples.set("4450", "issues/issue-4450");
examples.set("5274", "issues/issue-5274");
examples.set("ng-repo-1599", "issues/issue-ng-repo-1599");
+ examples.set("ng-repo-1626", "issues/issue-ng-repo-1626");
examples.set("6439", "issues/issue-6439");
return examples;
diff --git a/apps/app/ui-tests-app/modal-view/login-page.ts b/apps/app/ui-tests-app/modal-view/login-page.ts
index 2c4d5917f..86431d84b 100644
--- a/apps/app/ui-tests-app/modal-view/login-page.ts
+++ b/apps/app/ui-tests-app/modal-view/login-page.ts
@@ -1,32 +1,29 @@
-import * as pages from "tns-core-modules/ui/page";
-import * as textField from "tns-core-modules/ui/text-field";
-import * as observable from "tns-core-modules/data/observable";
+import { Page, ShownModallyData } from "tns-core-modules/ui/page";
+import { EventData, fromObject } from "tns-core-modules/data/observable";
-var context: any;
-var closeCallback: Function;
+export function onShowingModally(args: ShownModallyData) {
+ console.log("login-page.onShowingModally, context: " + args.context);
+ const page = args.object;
-var page: pages.Page;
-var usernameTextField: textField.TextField;
-var passwordTextField: textField.TextField;
-
-export function onShownModally(args: pages.ShownModallyData) {
- console.log("login-page.onShownModally, context: " + args.context);
- context = args.context;
- closeCallback = args.closeCallback;
+ page.bindingContext = fromObject({
+ username: "username",
+ password: "password",
+ context: args.context,
+ onLoginButtonTap: function() {
+ console.log("login-page.onLoginButtonTap");
+ args.closeCallback(this.username, this.password);
+ }
+ })
}
-export function onLoaded(args: observable.EventData) {
+export function onShownModally(args: ShownModallyData) {
+ console.log("login-page.onShownModally, context: " + args.context);
+}
+
+export function onLoaded(args: EventData) {
console.log("login-page.onLoaded");
- page = args.object;
- usernameTextField = page.getViewById("username");
- passwordTextField = page.getViewById("password");
}
export function onUnloaded() {
console.log("login-page.onUnloaded");
}
-
-export function onLoginButtonTap() {
- console.log("login-page.onLoginButtonTap");
- closeCallback(usernameTextField.text, passwordTextField.text);
-}
diff --git a/apps/app/ui-tests-app/modal-view/login-page.xml b/apps/app/ui-tests-app/modal-view/login-page.xml
index f2fd8c601..87c8487f3 100644
--- a/apps/app/ui-tests-app/modal-view/login-page.xml
+++ b/apps/app/ui-tests-app/modal-view/login-page.xml
@@ -1,8 +1,11 @@
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/apps/app/ui-tests-app/modal-view/modal-view.ts b/apps/app/ui-tests-app/modal-view/modal-view.ts
index 643377c5e..dec7ab7ab 100644
--- a/apps/app/ui-tests-app/modal-view/modal-view.ts
+++ b/apps/app/ui-tests-app/modal-view/modal-view.ts
@@ -16,9 +16,39 @@ export function onTapStretched(args) {
const label = page.getViewById("label");
var fullscreen = false;
var stretched = true;
-
+
page.showModal("ui-tests-app/modal-view/login-page", "context", function (username: string, password: string) {
console.log(username + "/" + password);
label.text = username + "/" + password;
}, fullscreen, false, stretched);
}
+
+function openModal(page: Page, label: Label, context: string) {
+ page.showModal("ui-tests-app/modal-view/login-page", context, function (username: string, password: string) {
+ const result = context + "/" + username + "/" + password;
+ console.log(result);
+ label.text = result;
+ }, false);
+}
+
+export function onTapSecondModalInCB(args) {
+ const page = args.object.page;
+ const label = page.getViewById("label");
+ page.showModal("ui-tests-app/modal-view/login-page", "First", function (username: string, password: string) {
+ const result = "First/" + username + "/" + password;
+ console.log(result);
+ label.text = result;
+
+ // Open second modal in the close callback of the first one.
+ openModal(page, label, "Second");
+ });
+}
+
+export function onTapSecondModalInTimer(args) {
+ const page = args.object.page;
+ const label = page.getViewById("label");
+ openModal(page, label, "First");
+
+ // Open second modal 1s after the first one.
+ setTimeout(() => openModal(page, label, "Second"), 1000);
+}
diff --git a/apps/app/ui-tests-app/modal-view/modal-view.xml b/apps/app/ui-tests-app/modal-view/modal-view.xml
index f33217964..44c980dcc 100644
--- a/apps/app/ui-tests-app/modal-view/modal-view.xml
+++ b/apps/app/ui-tests-app/modal-view/modal-view.xml
@@ -3,6 +3,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/apps/app/ui-tests-app/tab-view/main-page.ts b/apps/app/ui-tests-app/tab-view/main-page.ts
index ae3f6a4cf..57c80f6d7 100644
--- a/apps/app/ui-tests-app/tab-view/main-page.ts
+++ b/apps/app/ui-tests-app/tab-view/main-page.ts
@@ -24,6 +24,7 @@ export function loadExamples() {
examples.set("tab-view-bottom-position", "tab-view/tab-view-bottom-position");
examples.set("issue-5470", "tab-view/issue-5470");
examples.set("tab-view-tab-text-font-size", "tab-view/tab-view-tab-text-font-size");
+ examples.set("tab-view-android-swipe", "tab-view/tab-view-android-swipe");
examples.set("tab-view-icon-title-placement", "tab-view/icon-title-placement");
return examples;
}
diff --git a/apps/app/ui-tests-app/tab-view/tab-view-android-swipe.xml b/apps/app/ui-tests-app/tab-view/tab-view-android-swipe.xml
new file mode 100644
index 000000000..389c43127
--- /dev/null
+++ b/apps/app/ui-tests-app/tab-view/tab-view-android-swipe.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/.gitignore b/e2e/nested-frame-navigation/.gitignore
new file mode 100644
index 000000000..c6f9a448e
--- /dev/null
+++ b/e2e/nested-frame-navigation/.gitignore
@@ -0,0 +1 @@
+.vscode/settings.json
diff --git a/e2e/nested-frame-navigation/.vscode/launch.json b/e2e/nested-frame-navigation/.vscode/launch.json
new file mode 100644
index 000000000..a017624a8
--- /dev/null
+++ b/e2e/nested-frame-navigation/.vscode/launch.json
@@ -0,0 +1,100 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Launch Tests on Android",
+ "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
+ "args": [
+ "-u",
+ "tdd",
+ "--timeout",
+ "999999",
+ "--colors",
+ // "${workspaceFolder}/test",
+ "--opts",
+ "./e2e/config/mocha.opts",
+ "--runType",
+ "android23",
+ "--reuseDevice",
+ "--verbose"
+ ],
+ "internalConsoleOptions": "openOnSessionStart"
+ },
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Launch Tests on iOS",
+ "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
+ "args": [
+ "-u",
+ "tdd",
+ "--timeout",
+ "999999",
+ "--colors",
+ // "${workspaceFolder}/test",
+ "--opts",
+ "./e2e/config/mocha.opts",
+ "--runType",
+ "sim.iPhoneX.iOS112",
+ "--reuseDevice",
+ "--verbose"
+ ],
+ "internalConsoleOptions": "openOnSessionStart"
+ },
+ {
+ "name": "Launch on iOS",
+ "type": "nativescript",
+ "request": "launch",
+ "platform": "ios",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "stopOnEntry": false,
+ "tnsArgs": [
+ "--syncAllFiles"
+ ],
+ "watch": true
+ },
+ {
+ "name": "Attach on iOS",
+ "type": "nativescript",
+ "request": "attach",
+ "platform": "ios",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "tnsArgs": [
+ "--syncAllFiles"
+ ],
+ "watch": false
+ },
+ {
+ "name": "Launch on Android",
+ "type": "nativescript",
+ "request": "launch",
+ "platform": "android",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "stopOnEntry": false,
+ "tnsArgs": [
+ "--syncAllFiles"
+ ],
+ "watch": true
+ },
+ {
+ "name": "Attach on Android",
+ "type": "nativescript",
+ "request": "attach",
+ "platform": "android",
+ "appRoot": "${workspaceRoot}",
+ "sourceMaps": true,
+ "tnsArgs": [
+ "--syncAllFiles"
+ ],
+ "watch": false
+ }
+ ]
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/README.md b/e2e/nested-frame-navigation/README.md
new file mode 100644
index 000000000..99928960a
--- /dev/null
+++ b/e2e/nested-frame-navigation/README.md
@@ -0,0 +1,14 @@
+Execute Tests
+=============
+
+Android:
+
+```
+npm run e2e -- --runType android23 # --devMode
+```
+
+iOS:
+
+```
+npm run e2e -- --runType sim.iPhoneX.iOS112 # --devMode
+```
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/AndroidManifest.xml b/e2e/nested-frame-navigation/app/App_Resources/Android/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/Android/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/app.gradle b/e2e/nested-frame-navigation/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..058d81db4
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/Android/app.gradle
@@ -0,0 +1,16 @@
+// Add your native dependencies here:
+
+// Uncomment to add recyclerview-v7 dependency
+//dependencies {
+// compile 'com.android.support:recyclerview-v7:+'
+//}
+
+android {
+ defaultConfig {
+ generatedDensities = []
+ applicationId = "org.nativescript.nestedframenavigation"
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/background.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/background.png
new file mode 100644
index 000000000..eb381c258
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/background.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/icon.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/icon.png
new file mode 100755
index 000000000..9cde84cd5
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/icon.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/logo.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/logo.png
new file mode 100644
index 000000000..5218f4c90
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-hdpi/logo.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/background.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/background.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/icon.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/icon.png
new file mode 100755
index 000000000..4d6a674b3
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/icon.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/logo.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-ldpi/logo.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/background.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/background.png
new file mode 100644
index 000000000..efeaf2907
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/background.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/icon.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/icon.png
new file mode 100755
index 000000000..92ccc85a6
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/icon.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/logo.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/logo.png
new file mode 100644
index 000000000..626338766
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-mdpi/logo.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-nodpi/splash_screen.xml b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/background.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/background.png
new file mode 100644
index 000000000..612bbd072
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/background.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/icon.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..8bcde6277
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/icon.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/logo.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/logo.png
new file mode 100644
index 000000000..ad8ee2f4b
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xhdpi/logo.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/background.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/background.png
new file mode 100644
index 000000000..0fa88e235
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/background.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/icon.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/icon.png
new file mode 100644
index 000000000..9d81c85dc
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/icon.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/logo.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/logo.png
new file mode 100644
index 000000000..668327832
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxhdpi/logo.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/background.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/background.png
new file mode 100644
index 000000000..c650f6438
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/background.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/icon.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/icon.png
new file mode 100644
index 000000000..9a34d0d43
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/icon.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/logo.png b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/logo.png
new file mode 100644
index 000000000..fa6331c8d
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/Android/drawable-xxxhdpi/logo.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/values-v21/colors.xml b/e2e/nested-frame-navigation/app/App_Resources/Android/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/Android/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/values-v21/styles.xml b/e2e/nested-frame-navigation/app/App_Resources/Android/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/Android/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/values/colors.xml b/e2e/nested-frame-navigation/app/App_Resources/Android/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/Android/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #F5F5F5
+ #757575
+ #33B5E5
+ #272734
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/Android/values/styles.xml b/e2e/nested-frame-navigation/app/App_Resources/Android/values/styles.xml
new file mode 100644
index 000000000..c793e6d4c
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/Android/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..4034b76e6
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "icon-40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "icon-60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "icon-60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "icon-29.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "icon-29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "icon-40.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "icon-76.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "icon-76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "icon-83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "icon-1024.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png
new file mode 100644
index 000000000..a1d7eb479
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
new file mode 100644
index 000000000..bb9b9e86d
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
new file mode 100644
index 000000000..ec609dcf3
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
new file mode 100644
index 000000000..a97180800
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
new file mode 100644
index 000000000..214800ee6
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
new file mode 100644
index 000000000..8554b88a8
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
new file mode 100644
index 000000000..a22626bae
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
new file mode 100644
index 000000000..492c9c8e8
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
new file mode 100644
index 000000000..9208113cf
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
new file mode 100644
index 000000000..24415e5a3
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
new file mode 100644
index 000000000..b3ef1bf0c
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..11bfcf55c
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,176 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-1125h.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "filename" : "Default-Landscape-X.png",
+ "minimum-system-version" : "11.0",
+ "subtype" : "2436h",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Landscape@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "667h",
+ "filename" : "Default-667h@2x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "filename" : "Default-568h@2x.png",
+ "minimum-system-version" : "7.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default-568h@2x.png",
+ "extent" : "full-screen",
+ "subtype" : "retina4",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png
new file mode 100644
index 000000000..2913f85d9
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 000000000..d7f17fcd2
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
new file mode 100644
index 000000000..b88415405
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
new file mode 100644
index 000000000..faab4b631
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 000000000..cd94a3ac2
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
new file mode 100644
index 000000000..3365ba3cd
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
new file mode 100644
index 000000000..a44945c1a
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
new file mode 100644
index 000000000..e6dca6269
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
new file mode 100644
index 000000000..1a5007962
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
new file mode 100644
index 000000000..73d8b920f
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
new file mode 100644
index 000000000..514fc5cde
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-AspectFill.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-AspectFill@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
new file mode 100644
index 000000000..c293f9c7a
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
new file mode 100644
index 000000000..233693a6e
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-Center.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-Center@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
new file mode 100644
index 000000000..a5a775a2b
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
new file mode 100644
index 000000000..154c19343
Binary files /dev/null and b/e2e/nested-frame-navigation/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/Info.plist b/e2e/nested-frame-navigation/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/Info.plist
@@ -0,0 +1,47 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ ${PRODUCT_NAME}
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiresFullScreen
+
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/nested-frame-navigation/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..c4e5a3f39
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/app/App_Resources/iOS/build.xcconfig b/e2e/nested-frame-navigation/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..9d738435d
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/App_Resources/iOS/build.xcconfig
@@ -0,0 +1,7 @@
+// You can add custom settings here
+// for example you can uncomment the following line to force distribution code signing
+// CODE_SIGN_IDENTITY = iPhone Distribution
+// To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html
+// DEVELOPMENT_TEAM = YOUR_TEAM_ID;
+ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
diff --git a/e2e/nested-frame-navigation/app/app-root.xml b/e2e/nested-frame-navigation/app/app-root.xml
new file mode 100644
index 000000000..0455ca9ea
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/app-root.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/app.android.css b/e2e/nested-frame-navigation/app/app.android.css
new file mode 100644
index 000000000..2b5f529b5
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/app.android.css
@@ -0,0 +1,35 @@
+Button {
+ font-size: 8;
+ padding-left: 5;
+ padding-right: 5;
+ padding-top: 0;
+ padding-bottom: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-right: 0;
+ height: 50px;
+ color: blue;
+}
+
+TextView {
+ font-size: 10;
+ margin: 0;
+ padding: 0;
+ color: green;
+}
+
+Label {
+ font-size: 10;
+ margin: 0;
+ padding: 0;
+}
+
+GridLayout {
+ margin: 0;
+ padding: 0;
+}
+
+ActionBar {
+ height: 30;
+ margin: 0;
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/app.css b/e2e/nested-frame-navigation/app/app.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/e2e/nested-frame-navigation/app/app.ios.css b/e2e/nested-frame-navigation/app/app.ios.css
new file mode 100644
index 000000000..40458c5c6
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/app.ios.css
@@ -0,0 +1,7 @@
+Button{
+ color: blue;
+}
+
+TextView{
+ color: green;
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/app.ts b/e2e/nested-frame-navigation/app/app.ts
new file mode 100644
index 000000000..e0ef2193e
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/app.ts
@@ -0,0 +1,5 @@
+import * as application from "tns-core-modules/application";
+
+application.run({ moduleName: "app-root" });
+// application.run({ moduleName: "tab-root" });
+// application.run({ moduleName: "layout-root" });
diff --git a/e2e/nested-frame-navigation/app/frame-root/frame-home-page.ts b/e2e/nested-frame-navigation/app/frame-root/frame-home-page.ts
new file mode 100644
index 000000000..563f6f73d
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/frame-root/frame-home-page.ts
@@ -0,0 +1,46 @@
+import { EventData } from "tns-core-modules/ui/page";
+import { Button } from "tns-core-modules/ui/button";
+
+export function onNavigate(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("some-page/some-page");
+}
+
+export function onNavigateNone(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: false
+ });
+}
+
+export function onNavigateSlide(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: true,
+ transition: {
+ name: "slide",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
+
+export function onNavigateFlip(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: true,
+ transition: {
+ name: "flip",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
+
+export function onBackButtonTap(args: EventData): void {
+ const button = args.object;
+ button.page.frame.goBack();
+}
diff --git a/e2e/nested-frame-navigation/app/frame-root/frame-home-page.xml b/e2e/nested-frame-navigation/app/frame-root/frame-home-page.xml
new file mode 100644
index 000000000..082b9031c
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/frame-root/frame-home-page.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/frame-root/frame-multi-home-page.xml b/e2e/nested-frame-navigation/app/frame-root/frame-multi-home-page.xml
new file mode 100644
index 000000000..88d9c1371
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/frame-root/frame-multi-home-page.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/home/home-page.ts b/e2e/nested-frame-navigation/app/home/home-page.ts
new file mode 100644
index 000000000..5cac9f426
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/home/home-page.ts
@@ -0,0 +1,39 @@
+import * as application from "tns-core-modules/application";
+import { EventData } from "tns-core-modules/ui/core/view";
+import { Button } from "tns-core-modules/ui/button";
+
+export function onNavigateToLayoutFrame(args: EventData) {
+ application._resetRootView({ moduleName: "layout-root/layout-root-frame" });
+}
+
+export function onNavigateToLayoutMultiFrame(args: EventData) {
+ application._resetRootView({ moduleName: "layout-root/layout-root-multi-frame" });
+}
+
+export function onNavigateToPageFrame(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("frame-root/frame-home-page");
+}
+
+export function onNavigateToPageMultiFrame(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("frame-root/frame-multi-home-page");
+}
+
+export function onNavigateToTabsTopPage(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("tab-page/tabs-top-page");
+}
+
+export function onNavigateToTabsBottomPage(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("tab-page/tabs-bottom-page");
+}
+
+export function onNavigateToTabsTopRoot(args: EventData) {
+ application._resetRootView({ moduleName: "tab-root/tab-root-top" });
+}
+
+export function onNavigateToTabsBottomRoot(args: EventData) {
+ application._resetRootView({ moduleName: "tab-root/tab-root-bottom" });
+}
diff --git a/e2e/nested-frame-navigation/app/home/home-page.xml b/e2e/nested-frame-navigation/app/home/home-page.xml
new file mode 100644
index 000000000..229af254b
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/home/home-page.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/app/layout-root/layout-home-page.ts b/e2e/nested-frame-navigation/app/layout-root/layout-home-page.ts
new file mode 100644
index 000000000..d0667c28c
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/layout-root/layout-home-page.ts
@@ -0,0 +1,41 @@
+import { EventData } from "tns-core-modules/ui/page";
+import { Button } from "tns-core-modules/ui/button";
+
+export function onNavigate(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("some-page/some-page");
+}
+
+export function onNavigateNone(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: false
+ });
+}
+
+export function onNavigateSlide(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: true,
+ transition: {
+ name: "slide",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
+
+export function onNavigateFlip(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: true,
+ transition: {
+ name: "flip",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
diff --git a/e2e/nested-frame-navigation/app/layout-root/layout-home-page.xml b/e2e/nested-frame-navigation/app/layout-root/layout-home-page.xml
new file mode 100644
index 000000000..bde06d5af
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/layout-root/layout-home-page.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/layout-root/layout-home-secondary-page.ts b/e2e/nested-frame-navigation/app/layout-root/layout-home-secondary-page.ts
new file mode 100644
index 000000000..7b76efa9a
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/layout-root/layout-home-secondary-page.ts
@@ -0,0 +1,41 @@
+import { EventData } from "tns-core-modules/ui/page";
+import { Button } from "tns-core-modules/ui/button";
+
+export function onNavigate(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("other-page/other-page");
+}
+
+export function onNavigateNone(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "other-page/other-page",
+ animated: false
+ });
+}
+
+export function onNavigateSlide(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "other-page/other-page",
+ animated: true,
+ transition: {
+ name: "slide",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
+
+export function onNavigateFlip(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "other-page/other-page",
+ animated: true,
+ transition: {
+ name: "flip",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
diff --git a/e2e/nested-frame-navigation/app/layout-root/layout-home-secondary-page.xml b/e2e/nested-frame-navigation/app/layout-root/layout-home-secondary-page.xml
new file mode 100644
index 000000000..c99567b02
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/layout-root/layout-home-secondary-page.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/app/layout-root/layout-root-frame.ts b/e2e/nested-frame-navigation/app/layout-root/layout-root-frame.ts
new file mode 100644
index 000000000..f8fb1b3a3
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/layout-root/layout-root-frame.ts
@@ -0,0 +1,5 @@
+import * as application from "tns-core-modules/application";
+
+export function onReset() {
+ application._resetRootView({ moduleName: "app-root" });
+}
diff --git a/e2e/nested-frame-navigation/app/layout-root/layout-root-frame.xml b/e2e/nested-frame-navigation/app/layout-root/layout-root-frame.xml
new file mode 100644
index 000000000..cb4872897
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/layout-root/layout-root-frame.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/layout-root/layout-root-multi-frame.xml b/e2e/nested-frame-navigation/app/layout-root/layout-root-multi-frame.xml
new file mode 100644
index 000000000..12a38c9c7
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/layout-root/layout-root-multi-frame.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/other-page/other-page.ts b/e2e/nested-frame-navigation/app/other-page/other-page.ts
new file mode 100644
index 000000000..6847f8e84
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/other-page/other-page.ts
@@ -0,0 +1,7 @@
+import { EventData } from "tns-core-modules/ui/page";
+import { Button } from "tns-core-modules/ui/button";
+
+export function onBackButtonTap(args: EventData): void {
+ const button = args.object;
+ button.page.frame.goBack();
+}
diff --git a/e2e/nested-frame-navigation/app/other-page/other-page.xml b/e2e/nested-frame-navigation/app/other-page/other-page.xml
new file mode 100644
index 000000000..d98abe9b2
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/other-page/other-page.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/package.json b/e2e/nested-frame-navigation/app/package.json
new file mode 100644
index 000000000..0d6fdb754
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/package.json
@@ -0,0 +1,8 @@
+{
+ "android": {
+ "v8Flags": "--expose_gc"
+ },
+ "main": "app.js",
+ "name": "tns-template-hello-world-ts",
+ "version": "3.4.0"
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/players/player-item-detail/player-item-detail-page.ts b/e2e/nested-frame-navigation/app/players/player-item-detail/player-item-detail-page.ts
new file mode 100644
index 000000000..2e3643618
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/players/player-item-detail/player-item-detail-page.ts
@@ -0,0 +1,14 @@
+import { EventData, View } from "tns-core-modules/ui/core/view";
+import { NavigatedData, Page } from "tns-core-modules/ui/page";
+import { Item } from "../../shared/item";
+
+export function onNavigatingTo(args: NavigatedData) {
+ const page = args.object as Page;
+ const item = args.context as Item;
+ page.bindingContext = item;
+}
+
+export function onBackButtonTap(args: EventData) {
+ const view = args.object as View;
+ view.page.frame.goBack();
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/players/player-item-detail/player-item-detail-page.xml b/e2e/nested-frame-navigation/app/players/player-item-detail/player-item-detail-page.xml
new file mode 100644
index 000000000..8ad7d93b8
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/players/player-item-detail/player-item-detail-page.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/players/players-items-page.ts b/e2e/nested-frame-navigation/app/players/players-items-page.ts
new file mode 100644
index 000000000..55361a760
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/players/players-items-page.ts
@@ -0,0 +1,55 @@
+
+import { View } from "tns-core-modules/ui/core/view";
+import { ItemEventData } from "tns-core-modules/ui/list-view";
+import { NavigatedData, Page } from "tns-core-modules/ui/page";
+import { NavigationEntry } from "tns-core-modules/ui/frame";
+
+import { ItemsViewModel } from "../shared/items-view-model";
+import { Item } from "../shared/item";
+
+export function onNavigatingTo(args: NavigatedData) {
+ const page = args.object;
+ page.bindingContext = new ItemsViewModel(new Array- (
+ { id: 1, name: "Player One (default transition)", description: "Goalkeeper", transition: "default" },
+ { id: 2, name: "Player Two (default transition)", description: "Defender", transition: "default" },
+ { id: 3, name: "Player One (no transition)", description: "Goalkeeper", transition: "none" },
+ { id: 4, name: "Player Two (no transition)", description: "Defender", transition: "none" },
+ { id: 5, name: "Player One (slide transition)", description: "Goalkeeper", transition: "slide" },
+ { id: 6, name: "Player Two (slide transition)", description: "Defender", transition: "slide" },
+ { id: 7, name: "Player One (flip transition)", description: "Goalkeeper", transition: "flip" },
+ { id: 8, name: "Player Two (flip transition)", description: "Defender", transition: "flip" }
+ ));
+}
+
+export function onItemTap(args: ItemEventData) {
+ const view =
args.view;
+ const page = view.page;
+ const tappedItem = - view.bindingContext;
+
+ const entry: NavigationEntry = {
+ moduleName: "players/player-item-detail/player-item-detail-page",
+ context: tappedItem
+ };
+
+ switch (tappedItem.transition) {
+ case "none":
+ entry.animated = false;
+ break;
+ case "slide":
+ entry.transition = {
+ name: "slide",
+ duration: 380,
+ curve: "easeIn"
+ };
+ break;
+ case "flip":
+ entry.transition = {
+ name: "flip",
+ duration: 380,
+ curve: "easeIn"
+ };
+ break;
+ }
+
+ page.frame.navigate(entry);
+}
diff --git a/e2e/nested-frame-navigation/app/players/players-items-page.xml b/e2e/nested-frame-navigation/app/players/players-items-page.xml
new file mode 100644
index 000000000..95e9fa5ea
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/players/players-items-page.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/app/shared/item.ts b/e2e/nested-frame-navigation/app/shared/item.ts
new file mode 100644
index 000000000..15fbf8980
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/shared/item.ts
@@ -0,0 +1,6 @@
+export interface Item {
+ id: number;
+ name: string;
+ description: string;
+ transition: string;
+}
diff --git a/e2e/nested-frame-navigation/app/shared/items-view-model.ts b/e2e/nested-frame-navigation/app/shared/items-view-model.ts
new file mode 100644
index 000000000..f962c4f2f
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/shared/items-view-model.ts
@@ -0,0 +1,9 @@
+
+import { Observable } from "tns-core-modules/data/observable";
+import { Item } from "./item";
+
+export class ItemsViewModel extends Observable {
+ constructor(public items: Array- ) {
+ super();
+ }
+}
diff --git a/e2e/nested-frame-navigation/app/some-page/some-page.ts b/e2e/nested-frame-navigation/app/some-page/some-page.ts
new file mode 100644
index 000000000..6847f8e84
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/some-page/some-page.ts
@@ -0,0 +1,7 @@
+import { EventData } from "tns-core-modules/ui/page";
+import { Button } from "tns-core-modules/ui/button";
+
+export function onBackButtonTap(args: EventData): void {
+ const button =
args.object;
+ button.page.frame.goBack();
+}
diff --git a/e2e/nested-frame-navigation/app/some-page/some-page.xml b/e2e/nested-frame-navigation/app/some-page/some-page.xml
new file mode 100644
index 000000000..ead6a5b59
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/some-page/some-page.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/tab-page/tabs-bottom-page.xml b/e2e/nested-frame-navigation/app/tab-page/tabs-bottom-page.xml
new file mode 100644
index 000000000..03aa615a4
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/tab-page/tabs-bottom-page.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/tab-page/tabs-page.ts b/e2e/nested-frame-navigation/app/tab-page/tabs-page.ts
new file mode 100644
index 000000000..563f6f73d
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/tab-page/tabs-page.ts
@@ -0,0 +1,46 @@
+import { EventData } from "tns-core-modules/ui/page";
+import { Button } from "tns-core-modules/ui/button";
+
+export function onNavigate(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate("some-page/some-page");
+}
+
+export function onNavigateNone(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: false
+ });
+}
+
+export function onNavigateSlide(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: true,
+ transition: {
+ name: "slide",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
+
+export function onNavigateFlip(args: EventData) {
+ const button = args.object;
+ button.page.frame.navigate({
+ moduleName: "some-page/some-page",
+ animated: true,
+ transition: {
+ name: "flip",
+ duration: 380,
+ curve: "easeIn"
+ }
+ });
+}
+
+export function onBackButtonTap(args: EventData): void {
+ const button = args.object;
+ button.page.frame.goBack();
+}
diff --git a/e2e/nested-frame-navigation/app/tab-page/tabs-top-page.android.xml b/e2e/nested-frame-navigation/app/tab-page/tabs-top-page.android.xml
new file mode 100644
index 000000000..84d5c392b
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/tab-page/tabs-top-page.android.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/tab-root/tab-root-bottom.xml b/e2e/nested-frame-navigation/app/tab-root/tab-root-bottom.xml
new file mode 100644
index 000000000..a65f00a78
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/tab-root/tab-root-bottom.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/tab-root/tab-root-top.android.xml b/e2e/nested-frame-navigation/app/tab-root/tab-root-top.android.xml
new file mode 100644
index 000000000..aa39a85a6
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/tab-root/tab-root-top.android.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/tab-root/tab-root.ts b/e2e/nested-frame-navigation/app/tab-root/tab-root.ts
new file mode 100644
index 000000000..f8fb1b3a3
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/tab-root/tab-root.ts
@@ -0,0 +1,5 @@
+import * as application from "tns-core-modules/application";
+
+export function onReset() {
+ application._resetRootView({ moduleName: "app-root" });
+}
diff --git a/e2e/nested-frame-navigation/app/teams/team-item-detail/team-item-detail-page.ts b/e2e/nested-frame-navigation/app/teams/team-item-detail/team-item-detail-page.ts
new file mode 100644
index 000000000..2e3643618
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/teams/team-item-detail/team-item-detail-page.ts
@@ -0,0 +1,14 @@
+import { EventData, View } from "tns-core-modules/ui/core/view";
+import { NavigatedData, Page } from "tns-core-modules/ui/page";
+import { Item } from "../../shared/item";
+
+export function onNavigatingTo(args: NavigatedData) {
+ const page = args.object as Page;
+ const item = args.context as Item;
+ page.bindingContext = item;
+}
+
+export function onBackButtonTap(args: EventData) {
+ const view = args.object as View;
+ view.page.frame.goBack();
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/teams/team-item-detail/team-item-detail-page.xml b/e2e/nested-frame-navigation/app/teams/team-item-detail/team-item-detail-page.xml
new file mode 100644
index 000000000..c326f12a7
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/teams/team-item-detail/team-item-detail-page.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/app/teams/teams-items-page.ts b/e2e/nested-frame-navigation/app/teams/teams-items-page.ts
new file mode 100644
index 000000000..c0ea84471
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/teams/teams-items-page.ts
@@ -0,0 +1,55 @@
+
+import { View } from "tns-core-modules/ui/core/view";
+import { ItemEventData } from "tns-core-modules/ui/list-view";
+import { NavigatedData, Page } from "tns-core-modules/ui/page";
+import { NavigationEntry } from "tns-core-modules/ui/frame";
+
+import { ItemsViewModel } from "../shared/items-view-model";
+import { Item } from "../shared/item";
+
+export function onNavigatingTo(args: NavigatedData) {
+ const page = args.object;
+ page.bindingContext = new ItemsViewModel(new Array- (
+ { id: 1, name: "Team One (default transition)", description: "u17", transition: "default" },
+ { id: 2, name: "Team Two (default transition)", description: "u21", transition: "default" },
+ { id: 3, name: "Team One (no transition)", description: "u17", transition: "none" },
+ { id: 4, name: "Team Two (no transition)", description: "u21", transition: "none" },
+ { id: 5, name: "Team One (slide transition)", description: "u17", transition: "slide" },
+ { id: 6, name: "Team Two (slide transition)", description: "u21", transition: "slide" },
+ { id: 7, name: "Team One (flip transition)", description: "u17", transition: "flip" },
+ { id: 8, name: "Team Two (flip transition)", description: "u21", transition: "flip" }
+ ));
+}
+
+export function onItemTap(args: ItemEventData) {
+ const view =
args.view;
+ const page = view.page;
+ const tappedItem = - view.bindingContext;
+
+ const entry: NavigationEntry = {
+ moduleName: "teams/team-item-detail/team-item-detail-page",
+ context: tappedItem
+ };
+
+ switch (tappedItem.transition) {
+ case "none":
+ entry.animated = false;
+ break;
+ case "slide":
+ entry.transition = {
+ name: "slide",
+ duration: 380,
+ curve: "easeIn"
+ };
+ break;
+ case "flip":
+ entry.transition = {
+ name: "flip",
+ duration: 380,
+ curve: "easeIn"
+ };
+ break;
+ }
+
+ page.frame.navigate(entry);
+}
diff --git a/e2e/nested-frame-navigation/app/teams/teams-items-page.xml b/e2e/nested-frame-navigation/app/teams/teams-items-page.xml
new file mode 100644
index 000000000..b8ec24bd3
--- /dev/null
+++ b/e2e/nested-frame-navigation/app/teams/teams-items-page.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/nested-frame-navigation/e2e/config.ts b/e2e/nested-frame-navigation/e2e/config.ts
new file mode 100644
index 000000000..9f2147508
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/config.ts
@@ -0,0 +1,4 @@
+export const suspendTime = 1;
+export const appSuspendResume = true;
+export const dontKeepActivities = true;
+export const transitions = ["Default", "None", "Slide", "Flip"];
diff --git a/e2e/nested-frame-navigation/e2e/frame-root.e2e-spec.ts b/e2e/nested-frame-navigation/e2e/frame-root.e2e-spec.ts
new file mode 100644
index 000000000..fc8dca85d
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/frame-root.e2e-spec.ts
@@ -0,0 +1,255 @@
+import { AppiumDriver, createDriver } from "nativescript-dev-appium";
+import { Screen, playersData, home, somePage, teamsData } from "./screen";
+import * as shared from "./shared.e2e-spec";
+import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
+
+describe("frame-root:", () => {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async () => {
+ driver = await createDriver();
+ screen = new Screen(driver);
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(true);
+ }
+ });
+
+ after(async () => {
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(false);
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ transitions.forEach(transition => {
+ const playerOne = playersData[`playerOne${transition}`];
+ const playerTwo = playersData[`playerTwo${transition}`];
+ const teamOne = teamsData[`teamOne${transition}`];
+ const teamTwo = teamsData[`teamTwo${transition}`];
+
+ describe(`transition: ${transition} scenarios:`, () => {
+ it("loaded home page", async () => {
+ await screen.loadedHome();
+ });
+
+ it("loaded frame root with nested frame", async () => {
+ await screen.navigateToPageWithFrame();
+ await screen.loadedPageWithFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details and go back twice", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await shared.testPlayerNavigatedBack(screen, driver);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name) // wait for players list
+ }
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+ await shared.testPlayerNavigatedBack(screen, driver);
+ });
+
+ it("navigate parent frame and go back", async () => {
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details and navigate parent frame and go back", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded home page again", async () => {
+ await screen.goBackFromFrameHome();
+ await screen.loadedHome();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(home); // wait for home page
+ }
+ });
+
+ it("loaded frame root with multi nested frames", async () => {
+ await screen.navigateToPageWithMultiFrame();
+ await screen.loadedPageWithMultiFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded teams list", async () => {
+ await screen.loadedTeamsList();
+ });
+
+ it("loaded player details and go back twice", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await shared.testPlayerNavigatedBack(screen, driver);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name) // wait for players list
+ }
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+ await shared.testPlayerNavigatedBack(screen, driver);
+ });
+
+ it("navigate players parent frame and go back", async () => {
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded players details and navigate parent frame and go back", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayerDetails(playerTwo);
+ await screen.loadedTeamsList(); // assert visible & no changes
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded frame root with multi nested frames again", async () => {
+ await screen.loadedPageWithMultiFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded teams list", async () => {
+ await screen.loadedTeamsList();
+ });
+
+ it ("mix player and team list actions and go back", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamOne.name); // wait for teams list
+ }
+
+ await screen.loadedPlayerDetails(playerTwo); // assert no changes after back navigation
+ await screen.loadedTeamsList(); // assert no changes after back navigation
+
+ await shared.testTeamNavigated(teamTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamTwo.name); // wait for team
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayerDetails(playerTwo); // assert no changes after back navigation
+ await screen.loadedTeamDetails(teamTwo);
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+
+ await screen.goBackToTeamsList();
+ await screen.loadedTeamsList();
+ });
+
+ it("loaded home page again", async () => {
+ await screen.goBackFromFrameHome();
+ await screen.loadedHome();
+ });
+ });
+ });
+});
diff --git a/e2e/nested-frame-navigation/e2e/frame-tab-root.e2e-spec.ts b/e2e/nested-frame-navigation/e2e/frame-tab-root.e2e-spec.ts
new file mode 100644
index 000000000..fc1947b59
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/frame-tab-root.e2e-spec.ts
@@ -0,0 +1,223 @@
+import { AppiumDriver, createDriver } from "nativescript-dev-appium";
+import { Screen, playersData, somePage, teamsData } from "./screen";
+import * as shared from "./shared.e2e-spec";
+import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
+
+const roots = ["TabTop", "TabBottom"];
+
+function hyphenate(s: string) {
+ return s.replace(/([a-zA-Z])(?=[A-Z])/g, "$1-").toLowerCase();
+}
+
+describe("frame-tab-root:", () => {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async () => {
+ driver = await createDriver();
+ screen = new Screen(driver);
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(true);
+ }
+ });
+
+ after(async () => {
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(false);
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ roots.forEach(root => {
+ const rootWithHyphen = hyphenate(root);
+
+ describe(`${rootWithHyphen} scenarios:`, () => {
+
+ transitions.forEach(transition => {
+ const playerOne = playersData[`playerOne${transition}`];
+ const playerTwo = playersData[`playerTwo${transition}`];
+ const teamOne = teamsData[`teamOne${transition}`];
+ const teamTwo = teamsData[`teamTwo${transition}`];
+
+ describe(`transition: ${transition} scenarios:`, () => {
+
+ it("loaded home page", async () => {
+ await screen.loadedHome();
+ });
+
+ it(`loaded frame ${rootWithHyphen} root with nested frames`, async () => {
+ await screen[`navigateToPage${root}WithFrames`]();
+ await screen[`loadedPage${root}WithFrames`]();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details and go back twice", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await shared.testPlayerNavigatedBack(screen, driver);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name) // wait for players list
+ }
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+ await shared.testPlayerNavigatedBack(screen, driver);
+ });
+
+ it("navigate parent frame and go back", async () => {
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage) // wait for some page
+ }
+
+ await driver.navBack();
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details and navigate parent frame and go back", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack();
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+ });
+
+ it("toggle teams tab", async () => {
+ await screen.toggleTeamsTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamOne.name); // wait for team
+ }
+ });
+
+ it("loaded teams list", async () => {
+ await screen.loadedTeamsList();
+ });
+
+ it("mix player and team list actions and go back", async () => {
+ await screen.togglePlayersTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+
+ await screen.loadedPlayersList();
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.toggleTeamsTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamOne.name); // wait for teams list
+ }
+
+ await screen.loadedTeamsList();
+
+ await shared.testTeamNavigated(teamTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamTwo.name); // wait for team
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamTwo.name); // wait for team
+ }
+
+ await screen.loadedTeamDetails(teamTwo);
+
+ await screen.togglePlayersTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.toggleTeamsTab();
+
+ await screen.goBackToTeamsList();
+ await screen.loadedTeamsList();
+
+ await screen.togglePlayersTab();
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded home page again", async () => {
+ await screen[`goBackFrom${root}Page`]();
+ await screen.loadedHome();
+ });
+ });
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/e2e/layout-root.e2e-spec.ts b/e2e/nested-frame-navigation/e2e/layout-root.e2e-spec.ts
new file mode 100644
index 000000000..b1e7c44a9
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/layout-root.e2e-spec.ts
@@ -0,0 +1,483 @@
+import { AppiumDriver, createDriver } from "nativescript-dev-appium";
+import { Screen, playersData, home, somePage, otherPage, teamsData } from "./screen";
+import * as shared from "./shared.e2e-spec";
+import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
+
+describe("layout-root:", () => {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async () => {
+ driver = await createDriver();
+ screen = new Screen(driver);
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(true);
+ }
+ });
+
+ after(async () => {
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(false);
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ transitions.forEach(transition => {
+ const playerOne = playersData[`playerOne${transition}`];
+ const playerTwo = playersData[`playerTwo${transition}`];
+ const teamOne = teamsData[`teamOne${transition}`];
+
+ describe(`transition: ${transition} scenarios:`, () => {
+
+ it("loaded home page", async () => {
+ await screen.loadedHome();
+ });
+
+ it("loaded layout root with nested frames", async () => {
+ await screen.navigateToLayoutWithFrame();
+ await screen.loadedLayoutWithFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details and go back twice", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await shared.testPlayerNavigatedBack(screen, driver);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+ await shared.testPlayerNavigatedBack(screen, driver);
+ });
+
+ it("navigate parent frame and go back", async () => {
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details and navigate parent frame and go back", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded home page again", async () => {
+ await screen.resetToHome();
+ await screen.loadedHome();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(home); // wait for home page
+ }
+ });
+
+ it("loaded layout root with multi nested frames", async () => {
+ await screen.navigateToLayoutWithMultiFrame();
+ await screen.loadedLayoutWithMultiFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded teams list", async () => {
+ await screen.loadedTeamsList();
+ });
+
+ it("loaded player details and go back twice", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await shared.testPlayerNavigatedBack(screen, driver);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name) // wait for players list
+ }
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+ await shared.testPlayerNavigatedBack(screen, driver);
+ });
+
+ it("navigate players parent frame and go back", async () => {
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded players details and navigate parent frame and go back", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded layout root with multi nested frames again", async () => {
+ await screen.loadedLayoutWithMultiFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded teams list", async () => {
+ await screen.loadedTeamsList();
+ });
+
+ it ("mix player and team list actions and go back", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await shared[`testOtherPageNavigated${transition}`](screen); // "teams" parent frame navigation
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(otherPage); // wait for other page
+ }
+
+ await screen.loadedPlayerDetails(playerTwo); // assert no changes in the sibling frame
+
+ await driver.navBack(); // other page back navigation
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamOne.name); // wait for teams list
+ }
+
+ await screen.loadedTeamsList();
+ await screen.loadedPlayerDetails(playerTwo); // assert no changes in the sibling frame
+
+ await shared[`testOtherPageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(otherPage); // wait for other page
+ }
+
+ await shared[`testSomePageNavigated${transition}`](screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await screen.loadedOtherPage(); // assert no changes in the sibling frame
+
+ await driver.navBack(); // some page back navigation
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+
+ await screen.goBackFromOtherPage();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamOne.name); // wait for team
+ }
+
+ await screen.loadedTeamsList();
+ });
+
+ it("loaded home page again", async () => {
+ await screen.resetToHome();
+ await screen.loadedHome();
+ });
+ });
+ });
+
+ describe("players list slide transition with parent frame default transition:", () => {
+ const playerOne = playersData["playerOneSlide"];
+ const playerTwo = playersData["playerTwoSlide"];
+
+ it("loaded layout root with nested frames", async () => {
+ await screen.navigateToLayoutWithFrame();
+ await screen.loadedLayoutWithFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details with slide", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+ });
+
+ it("navigate parent frame and go back", async () => {
+ await shared.testSomePageNavigatedDefault(screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await screen.loadedPlayerDetails(playerTwo);
+ });
+
+ it("loaded player list", async () => {
+ await screen.goBackToPlayersList();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+ });
+
+ it("loaded home page again", async () => {
+ await screen.resetToHome();
+ await screen.loadedHome();
+ });
+ });
+
+ describe("players list slide transition with parent frame no transition:", () => {
+ const playerOne = playersData["playerOneSlide"];
+ const playerTwo = playersData["playerTwoSlide"];
+
+ it("loaded layout root with nested frames", async () => {
+ await screen.navigateToLayoutWithFrame();
+ await screen.loadedLayoutWithFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details with slide", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+ });
+
+ it("navigate parent frame and go back", async () => {
+ await shared.testSomePageNavigatedNone(screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await screen.loadedPlayerDetails(playerTwo);
+ });
+
+ it("loaded player list", async () => {
+ await screen.goBackToPlayersList();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+ });
+
+ it("loaded home page again", async () => {
+ await screen.resetToHome();
+ await screen.loadedHome();
+ });
+ });
+
+ describe("players list flip transition with parent frame default transition:", () => {
+ const playerOne = playersData["playerOneFlip"];
+ const playerTwo = playersData["playerTwoFlip"];
+
+ it("loaded layout root with nested frames", async () => {
+ await screen.navigateToLayoutWithFrame();
+ await screen.loadedLayoutWithFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details with slide", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+ });
+
+ it("navigate parent frame and go back", async () => {
+ await shared.testSomePageNavigatedDefault(screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await screen.loadedPlayerDetails(playerTwo);
+ });
+
+ it("loaded player list", async () => {
+ await screen.goBackToPlayersList();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+ });
+
+ it("loaded home page again", async () => {
+ await screen.resetToHome();
+ await screen.loadedHome();
+ });
+ });
+
+ describe("players list flip transition with parent frame no transition:", () => {
+ const playerOne = playersData["playerOneFlip"];
+ const playerTwo = playersData["playerTwoFlip"];
+
+ it("loaded layout root with nested frames", async () => {
+ await screen.navigateToLayoutWithFrame();
+ await screen.loadedLayoutWithFrame();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details with slide", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+ });
+
+ it("navigate parent frame and go back", async () => {
+ await shared.testSomePageNavigatedNone(screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(somePage); // wait for some page
+ }
+
+ await driver.navBack(); // some page back navigation
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name); // wait for player
+ }
+
+ await screen.loadedPlayerDetails(playerTwo);
+ });
+
+ it("loaded player list", async () => {
+ await screen.goBackToPlayersList();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name); // wait for players list
+ }
+ });
+
+ it("loaded home page again", async () => {
+ await screen.resetToHome();
+ await screen.loadedHome();
+ });
+ });
+});
diff --git a/e2e/nested-frame-navigation/e2e/screen.ts b/e2e/nested-frame-navigation/e2e/screen.ts
new file mode 100644
index 000000000..ad79a98f8
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/screen.ts
@@ -0,0 +1,355 @@
+import { AppiumDriver } from "nativescript-dev-appium";
+import { assert } from "chai";
+
+const layoutWithFrame = "Layout w/ frame";
+const layoutWithMultiFrame = "Layout w/ multi frame";
+const pageWithFrame = "Page w/ frame";
+const pageWithMultiFrame = "Page w/ multi frame";
+const pageTabTopWithFrames = "Page w/ tabs (top)";
+const pageTabBottomWithFrames = "Page w/ tabs (bottom)";
+const tabTopRootWithFrames = "Root tabs (top)";
+const tabBottomRootWithFrames = "Root tabs (bottom)";
+const layoutHome = "layout home page";
+const layoutHomeSecondary = "layout home secondary page";
+const frameHome = "frame home page";
+const frameHomeSecondary = "multi frame home page";
+const tabTopHome = "tab top page";
+const tabBottomHome = "tab bottom page";
+const tabRootTopHome = "tab root top home";
+const tabRootBottomHome = "tab root bottom home";
+const navigateToSomePageDefault = "navigate to some page (default transition)";
+const navigateToSomePageNone = "navigate to some page (no transition)";
+const navigateToSomePageSlide = "navigate to some page (slide transition)";
+const navigateToSomePageFlip = "navigate to some page (flip transition)";
+const navigateToOtherPageDefault = "navigate to other page (default transition)";
+const navigateToOtherPageNone = "navigate to other page (no transition)";
+const navigateToOtherPageSlide = "navigate to other page (slide transition)";
+const navigateToOtherPageFlip = "navigate to other page (flip transition)";
+const players = "Players";
+const teams = "Teams";
+const playerBack = "playerBack";
+const somePageBack = "somePageBack";
+const otherPageBack = "otherPageBack";
+const teamBack = "teamBack";
+const frameHomeBack = "frameHomeBack";
+const tabTopBack = "tabTopBack";
+const tabBottomBack = "tabBottomBack";
+const resetApp = "reset app";
+
+export const home = "Home";
+export const somePage = "some page";
+export const otherPage = "other page";
+
+export const playersData = {
+ playerOneDefault: {
+ name: "Player One (default transition)",
+ description: "Goalkeeper",
+ transition: "default"
+ },
+ playerTwoDefault: {
+ name: "Player Two (default transition)",
+ description: "Defender",
+ transition: "default"
+ },
+ playerOneNone: {
+ name: "Player One (no transition)",
+ description: "Goalkeeper",
+ transition: "none"
+ },
+ playerTwoNone: {
+ name: "Player Two (no transition)",
+ description: "Defender",
+ transition: "none"
+ },
+ playerOneSlide: {
+ name: "Player One (slide transition)",
+ description: "Goalkeeper",
+ transition: "slide"
+ },
+ playerTwoSlide: {
+ name: "Player Two (slide transition)",
+ description: "Defender",
+ transition: "slide"
+ },
+ playerOneFlip: {
+ name: "Player One (flip transition)",
+ description: "Goalkeeper",
+ transition: "flip"
+ },
+ playerTwoFlip: {
+ name: "Player Two (flip transition)",
+ description: "Defender",
+ transition: "flip"
+ }
+}
+
+export const teamsData = {
+ teamOneDefault: {
+ name: "Team One (default transition)",
+ description: "u17",
+ transition: "default"
+ },
+ teamTwoDefault: {
+ name: "Team Two (default transition)",
+ description: "u21",
+ transition: "default"
+ },
+ teamOneNone: {
+ name: "Team One (no transition)",
+ description: "u17",
+ transition: "none"
+ },
+ teamTwoNone: {
+ name: "Team Two (no transition)",
+ description: "u21",
+ transition: "none"
+ },
+ teamOneSlide: {
+ name: "Team One (slide transition)",
+ description: "u17",
+ transition: "slide"
+ },
+ teamTwoSlide: {
+ name: "Team Two (slide transition)",
+ description: "u21",
+ transition: "slide"
+ },
+ teamOneFlip: {
+ name: "Team One (flip transition)",
+ description: "u17",
+ transition: "flip"
+ },
+ teamTwoFlip: {
+ name: "Team Two (flip transition)",
+ description: "u21",
+ transition: "flip"
+ },
+}
+export interface Item {
+ name: string;
+ description: string;
+}
+
+export class Screen {
+
+ private _driver: AppiumDriver
+
+ public currentAnimation: string;
+
+ constructor(driver: AppiumDriver) {
+ this._driver = driver;
+ }
+
+ navigateToLayoutWithFrame = async () => {
+ await this.navigateToPage(layoutWithFrame);
+ };
+
+ navigateToLayoutWithMultiFrame = async () => {
+ await this.navigateToPage(layoutWithMultiFrame);
+ }
+
+ navigateToPageWithFrame = async () => {
+ await this.navigateToPage(pageWithFrame);
+ }
+
+ navigateToPageWithMultiFrame = async () => {
+ await this.navigateToPage(pageWithMultiFrame);
+ }
+
+ navigateToPageTabTopWithFrames = async () => {
+ await this.navigateToPage(pageTabTopWithFrames);
+ }
+
+ navigateToPageTabBottomWithFrames = async () => {
+ await this.navigateToPage(pageTabBottomWithFrames);
+ }
+
+ navigateToTabTopRootWithFrames = async () => {
+ await this.navigateToPage(tabTopRootWithFrames);
+ }
+
+ navigateToTabBottomRootWithFrames = async () => {
+ await this.navigateToPage(tabBottomRootWithFrames);
+ }
+
+ navigateToSomePageDefault = async () => {
+ await this.navigateToPage(navigateToSomePageDefault);
+ };
+
+ navigateToSomePageNone = async () => {
+ await this.navigateToPage(navigateToSomePageNone);
+ };
+
+ navigateToSomePageSlide = async () => {
+ await this.navigateToPage(navigateToSomePageSlide);
+ };
+
+ navigateToSomePageFlip = async () => {
+ await this.navigateToPage(navigateToSomePageFlip);
+ };
+
+ navigateToOtherPageDefault = async () => {
+ await this.navigateToPage(navigateToOtherPageDefault);
+ }
+
+ navigateToOtherPageNone = async () => {
+ await this.navigateToPage(navigateToOtherPageNone);
+ }
+
+ navigateToOtherPageSlide = async () => {
+ await this.navigateToPage(navigateToOtherPageSlide);
+ }
+
+ navigateToOtherPageFlip = async () => {
+ await this.navigateToPage(navigateToOtherPageFlip);
+ }
+
+ navigateToPlayerDetails = async (player: Item) => {
+ await this.navigateToItem(player);
+ };
+
+ navigateToTeamDetails = async (team: Item) => {
+ await this.navigateToItem(team);
+ };
+
+ resetToHome = async () => {
+ const btnReset = await this._driver.findElementByAutomationText(resetApp);
+ await btnReset.tap();
+ };
+
+ goBackToPlayersList = async () => {
+ await this.goBack(playerBack);
+ };
+
+ goBackToTeamsList = async () => {
+ await this.goBack(teamBack);
+ };
+
+ goBackFromSomePage = async () => {
+ await this.goBack(somePageBack);
+ }
+
+ goBackFromOtherPage = async () => {
+ await this.goBack(otherPageBack);
+ }
+
+ goBackFromFrameHome = async () => {
+ await this.goBack(frameHomeBack);
+ }
+
+ goBackFromTabTopPage = async () => {
+ await this.goBack(tabTopBack);
+ }
+
+ goBackFromTabBottomPage = async () => {
+ await this.goBack(tabBottomBack);
+ }
+
+ togglePlayersTab = async () => {
+ const lblPlayers = await this._driver.findElementByAutomationText(players);
+ await lblPlayers.tap();
+ }
+
+ toggleTeamsTab = async () => {
+ const lblTeams = await this._driver.findElementByAutomationText(teams);
+ await lblTeams.tap();
+ }
+
+ loadedHome = async () => {
+ const lblHome = await this._driver.findElementByAutomationText(home);
+ assert.isTrue(await lblHome.isDisplayed());
+ console.log(home + " loaded!");
+ };
+
+ loadedLayoutWithFrame = async () => {
+ await this.loadedPage(layoutHome);
+ };
+
+ loadedLayoutWithMultiFrame = async () => {
+ await this.loadedPage(layoutHome);
+ await this.loadedPage(layoutHomeSecondary);
+ };
+
+ loadedPageWithFrame = async () => {
+ await this.loadedPage(frameHome);
+ }
+
+ loadedPageWithMultiFrame = async () => {
+ await this.loadedPage(frameHomeSecondary);
+ }
+
+ loadedPageTabTopWithFrames = async () => {
+ await this.loadedPage(tabTopHome);
+ }
+
+ loadedPageTabBottomWithFrames = async () => {
+ await this.loadedPage(tabBottomHome);
+ }
+
+ loadedTabTopRootWithFrames = async () => {
+ await this.loadedPage(tabRootTopHome);
+ }
+
+ loadedTabBottomRootWithFrames = async () => {
+ await this.loadedPage(tabRootBottomHome);
+ }
+
+ loadedSomePage = async () => {
+ await this.loadedPage(somePage);
+ }
+
+ loadedOtherPage = async () => {
+ await this.loadedPage(otherPage);
+ }
+
+ loadedPlayersList = async () => {
+ const lblPlayerOne = await this._driver.findElementByAutomationText(playersData["playerOneDefault"].name);
+ assert.isTrue(await lblPlayerOne.isDisplayed());
+ console.log(players + " loaded!");
+ }
+
+ loadedPlayerDetails = async (player: Item) => {
+ await this.loadedItem(player);
+ }
+
+ loadedTeamsList = async () => {
+ const lblTeamOne = await this._driver.findElementByAutomationText(teamsData["teamOneDefault"].name);
+ assert.isTrue(await lblTeamOne.isDisplayed());
+ console.log(teams + " loaded!");
+ }
+
+ loadedTeamDetails = async (team: Item) => {
+ await this.loadedItem(team);
+ }
+
+ private navigateToPage = async (page: string) => {
+ const btnPage = await this._driver.findElementByAutomationText(page);
+ await btnPage.tap();
+ };
+
+ private loadedPage = async (page: string) => {
+ const lblPage = await this._driver.findElementByAutomationText(page);
+ assert.isTrue(await lblPage.isDisplayed());
+ console.log(page + " loaded!");
+ };
+
+ private navigateToItem = async (item: Item) => {
+ const lblItem = await this._driver.findElementByAutomationText(item.name);
+ await lblItem.tap();
+ }
+
+ private loadedItem = async (item: Item) => {
+ const lblItemName = await this._driver.findElementByAutomationText(item.name);
+ assert.isTrue(await lblItemName.isDisplayed());
+
+ const lblItemDescription = await this._driver.findElementByAutomationText(item.description);
+ assert.isTrue(await lblItemDescription.isDisplayed());
+
+ console.log(item.name + " loaded!");
+ }
+
+ private goBack = async (accessibilityId: string) => {
+ const btnBack = await this._driver.findElementByAccessibilityId(accessibilityId);
+ await btnBack.tap();
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/e2e/setup.ts b/e2e/nested-frame-navigation/e2e/setup.ts
new file mode 100644
index 000000000..8b26e66e9
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/setup.ts
@@ -0,0 +1,9 @@
+import { startServer, stopServer } from "nativescript-dev-appium";
+
+before("start server", async () => {
+ await startServer();
+});
+
+after("stop server", async () => {
+ await stopServer();
+});
diff --git a/e2e/nested-frame-navigation/e2e/shared.e2e-spec.ts b/e2e/nested-frame-navigation/e2e/shared.e2e-spec.ts
new file mode 100644
index 000000000..b556ae804
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/shared.e2e-spec.ts
@@ -0,0 +1,62 @@
+import { Screen, Item } from "./screen";
+import { AppiumDriver } from "nativescript-dev-appium";
+
+export async function testPlayerNavigated(player: Item, screen: Screen) {
+ await screen.navigateToPlayerDetails(player);
+ await screen.loadedPlayerDetails(player);
+}
+
+export async function testPlayerNavigatedBack(screen: Screen, driver: AppiumDriver) {
+ await driver.navBack();
+ await screen.loadedPlayersList();
+}
+
+export async function testSomePageNavigatedDefault(screen: Screen) {
+ await screen.navigateToSomePageDefault();
+ await screen.loadedSomePage();
+}
+
+export async function testSomePageNavigatedNone(screen: Screen) {
+ await screen.navigateToSomePageNone();
+ await screen.loadedSomePage();
+}
+
+export async function testSomePageNavigatedSlide(screen: Screen) {
+ await screen.navigateToSomePageSlide();
+ await screen.loadedSomePage();
+}
+
+export async function testSomePageNavigatedFlip(screen: Screen) {
+ await screen.navigateToSomePageFlip();
+ await screen.loadedSomePage();
+}
+
+export async function testTeamNavigated(team: Item, screen: Screen) {
+ await screen.navigateToTeamDetails(team);
+ await screen.loadedTeamDetails(team);
+}
+
+export async function testTeamNavigatedBack(screen: Screen, driver: AppiumDriver) {
+ await driver.navBack();
+ await screen.loadedTeamsList();
+}
+
+export async function testOtherPageNavigatedDefault(screen: Screen) {
+ await screen.navigateToOtherPageDefault();
+ await screen.loadedOtherPage();
+}
+
+export async function testOtherPageNavigatedNone(screen: Screen) {
+ await screen.navigateToOtherPageNone();
+ await screen.loadedOtherPage();
+}
+
+export async function testOtherPageNavigatedSlide(screen: Screen) {
+ await screen.navigateToOtherPageSlide();
+ await screen.loadedOtherPage();
+}
+
+export async function testOtherPageNavigatedFlip(screen: Screen) {
+ await screen.navigateToOtherPageFlip();
+ await screen.loadedOtherPage();
+}
diff --git a/e2e/nested-frame-navigation/e2e/tab-root.e2e-spec.ts b/e2e/nested-frame-navigation/e2e/tab-root.e2e-spec.ts
new file mode 100644
index 000000000..57e546919
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/tab-root.e2e-spec.ts
@@ -0,0 +1,157 @@
+import { AppiumDriver, createDriver } from "nativescript-dev-appium";
+import { Screen, playersData, teamsData } from "./screen";
+import * as shared from "./shared.e2e-spec";
+import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
+
+const roots = ["TabTop", "TabBottom"];
+
+function hyphenate(s: string) {
+ return s.replace(/([a-zA-Z])(?=[A-Z])/g, "$1-").toLowerCase();
+}
+
+describe("tab-root:", () => {
+ let driver: AppiumDriver;
+ let screen: Screen;
+
+ before(async () => {
+ driver = await createDriver();
+ screen = new Screen(driver);
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(true);
+ }
+ });
+
+ after(async () => {
+ if (dontKeepActivities) {
+ await driver.setDontKeepActivities(false);
+ }
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logTestArtifacts(this.currentTest.title);
+ }
+ });
+
+ roots.forEach(root => {
+ const rootWithHyphen = hyphenate(root);
+
+ describe(`${rootWithHyphen} scenarios:`, () => {
+
+ transitions.forEach(transition => {
+ const playerOne = playersData[`playerOne${transition}`];
+ const playerTwo = playersData[`playerTwo${transition}`];
+ const teamOne = teamsData[`teamOne${transition}`];
+ const teamTwo = teamsData[`teamTwo${transition}`];
+
+ describe(`transition: ${transition} scenarios:`, () => {
+
+ it("loaded home page", async () => {
+ await screen.loadedHome();
+ });
+
+ it(`loaded ${rootWithHyphen} root with frames`, async () => {
+ await screen[`navigateTo${root}RootWithFrames`]();
+ await screen[`loaded${root}RootWithFrames`]();
+ });
+
+ it("loaded players list", async () => {
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded player details and go back twice", async () => {
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await shared.testPlayerNavigatedBack(screen, driver);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name) // wait for players list
+ }
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+ await shared.testPlayerNavigatedBack(screen, driver);
+ });
+
+ it("toggle teams tab", async () => {
+ await screen.toggleTeamsTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamOne.name) // wait for teams list
+ }
+ });
+
+ it("loaded teams list", async () => {
+ await screen.loadedTeamsList();
+ });
+
+ it("mix player and team list actions and go back", async () => {
+ await screen.togglePlayersTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerOne.name) // wait for players list
+ }
+
+ await screen.loadedPlayersList();
+
+ await shared.testPlayerNavigated(playerTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await screen.toggleTeamsTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamOne.name) // wait for teams list
+ }
+
+ await screen.loadedTeamsList();
+
+ await shared.testTeamNavigated(teamTwo, screen);
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(teamTwo.name) // wait for team
+ }
+
+ await screen.togglePlayersTab();
+
+ if (appSuspendResume) {
+ await driver.backgroundApp(suspendTime);
+ await driver.waitForElement(playerTwo.name) // wait for player
+ }
+
+ await screen.loadedPlayerDetails(playerTwo);
+
+ await screen.toggleTeamsTab();
+
+ await screen.goBackToTeamsList();
+ await screen.loadedTeamsList();
+
+ await screen.togglePlayersTab();
+
+ await screen.goBackToPlayersList();
+ await screen.loadedPlayersList();
+ });
+
+ it("loaded home page again", async () => {
+ await screen.resetToHome();
+ await screen.loadedHome();
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/e2e/nested-frame-navigation/e2e/tsconfig.json b/e2e/nested-frame-navigation/e2e/tsconfig.json
new file mode 100644
index 000000000..6517ca58d
--- /dev/null
+++ b/e2e/nested-frame-navigation/e2e/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "importHelpers": false,
+ "sourceMap": true,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ],
+ "lib": [
+ "es2015",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/package.json b/e2e/nested-frame-navigation/package.json
new file mode 100644
index 000000000..42a146577
--- /dev/null
+++ b/e2e/nested-frame-navigation/package.json
@@ -0,0 +1,36 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.nestedframenavigation",
+ "tns-ios": {
+ "version": "next"
+ },
+ "tns-android": {
+ "version": "next"
+ }
+ },
+ "dependencies": {
+ "nativescript-theme-core": "~1.0.4",
+ "tns-core-modules": "next"
+ },
+ "devDependencies": {
+ "@types/chai": "~4.1.3",
+ "@types/mocha": "~5.2.1",
+ "@types/node": "^7.0.5",
+ "mocha": "^5.2.0",
+ "mocha-junit-reporter": "^1.18.0",
+ "mocha-multi": "^1.0.1",
+ "nativescript-dev-appium": "next",
+ "nativescript-dev-typescript": "next",
+ "nativescript-dev-webpack": "next",
+ "rimraf": "^2.6.2"
+ },
+ "scripts": {
+ "e2e": "npm run clean-e2e && tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
+ "e2e-watch": "tsc -p e2e --watch",
+ "clean-e2e": "rimraf 'e2e/**/*.js' 'e2e/**/*.js.map' 'e2e/**/*.map'"
+ }
+}
\ No newline at end of file
diff --git a/e2e/nested-frame-navigation/tsconfig.json b/e2e/nested-frame-navigation/tsconfig.json
new file mode 100644
index 000000000..15a5ce832
--- /dev/null
+++ b/e2e/nested-frame-navigation/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "lib": [
+ "es6",
+ "dom"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "*": [
+ "./node_modules/tns-core-modules/*",
+ "./node_modules/*"
+ ],
+ "~/*": [
+ "app/*"
+ ]
+ }
+ },
+ "exclude": [
+ "node_modules",
+ "platforms",
+ "e2e"
+ ]
+}
\ No newline at end of file
diff --git a/merge-guidance-schema.png b/merge-guidance-schema.png
new file mode 100644
index 000000000..0e83bc8fd
Binary files /dev/null and b/merge-guidance-schema.png differ
diff --git a/release-contribution-guide-schema.png b/release-contribution-guide-schema.png
new file mode 100644
index 000000000..9ce519055
Binary files /dev/null and b/release-contribution-guide-schema.png differ
diff --git a/tests/app/ui/button/button-tests.ts b/tests/app/ui/button/button-tests.ts
index e9f4caeff..1af31325d 100644
--- a/tests/app/ui/button/button-tests.ts
+++ b/tests/app/ui/button/button-tests.ts
@@ -404,3 +404,18 @@ export function test_setting_formattedText_With_UnknownFont_DoesNotCrash() {
TKUnit.waitUntilReady(() => btn.isLayoutValid);
});
}
+
+export function test_Native_Background_Color_BorderRadius_Change() {
+ let view = new buttonModule.Button();
+ view.text = "TEST";
+ helper.buildUIAndRunTest(view, function (views: Array) {
+ let page = views[1];
+ page.css = ".border { background-color: #00FF00; border-radius: 1; } .colorfilter { background-color: #FF0000; }";
+ view.className = "border";
+ helper.waitUntilLayoutReady(view);
+ TKUnit.assertEqual(buttonTestsNative.getNativeBackgroundColor(view).hex, "#00FF00");
+ view.className = "colorfilter";
+ helper.waitUntilLayoutReady(view);
+ TKUnit.assertEqual(buttonTestsNative.getNativeBackgroundColor(view).hex, "#FF0000");
+ });
+}
diff --git a/tests/app/ui/helper.ts b/tests/app/ui/helper.ts
index 0f8509712..74bc6dd4a 100644
--- a/tests/app/ui/helper.ts
+++ b/tests/app/ui/helper.ts
@@ -150,9 +150,13 @@ export function buildUIWithWeakRefAndInteract(createFunc: () =>
// Could cause GC on the next call.
// NOTE: Don't replace this with forceGC();
new ArrayBuffer(4 * 1024 * 1024);
+
+ // An additional GC and wait are needed since WebKit upgrade to version 12.0
+ // (TEXT-FIELD.testMemoryLeak test started failing sporadically)
+ utils.GC();
+ TKUnit.wait(0.1);
}
utils.GC();
-
try {
TKUnit.assert(!weakRef.get(), weakRef.get() + " leaked!");
done(null);
@@ -683,13 +687,13 @@ function setupSetters(): void {
cssSetters.set("selectedTabTextColor", "red");
cssSetters.set("androidSelectedTabHighlightColor", "red");
- // ListView-specific props
+ // ListView-specific props
cssSetters.set("separatorColor", "red");
// SegmentedBar-specific props
cssSetters.set("selectedBackgroundColor", "red");
- // Page-specific props
+ // Page-specific props
cssSetters.set("statusBarStyle", "light");
cssSetters.set("androidStatusBarBackground", "red");
@@ -855,4 +859,4 @@ function setupSetters(): void {
return text ? text.toString() : text;
}
});
-}
\ No newline at end of file
+}
diff --git a/tests/app/ui/page/page-tests-common.ts b/tests/app/ui/page/page-tests-common.ts
index e978aadb1..7275ca10f 100644
--- a/tests/app/ui/page/page-tests-common.ts
+++ b/tests/app/ui/page/page-tests-common.ts
@@ -738,6 +738,7 @@ export function test_WhenPageIsNavigatedToItCanShowAnotherPageAsModal() {
TKUnit.assertTrue(ctx.shownModally, "Modal-page must be shown!");
TKUnit.assertEqual(returnValue, "return value", "Modal-page must return value!");
modalClosed = true;
+ TKUnit.assertNull(masterPage.modal, "currentPage.modal should be undefined when no modal page is shown!");
}
let modalPage: Page;
@@ -758,7 +759,6 @@ export function test_WhenPageIsNavigatedToItCanShowAnotherPageAsModal() {
const onModalUnloaded = function (args: EventData) {
modalUnloaded++;
modalPage.off(Page.unloadedEvent, onModalUnloaded);
- TKUnit.assertNull(masterPage.modal, "currentPage.modal should be undefined when no modal page is shown!");
}
const navigatedToEventHandler = function (args) {
diff --git a/tests/app/ui/page/page-tests.ios.ts b/tests/app/ui/page/page-tests.ios.ts
index 88b6c498e..34bbed8b6 100644
--- a/tests/app/ui/page/page-tests.ios.ts
+++ b/tests/app/ui/page/page-tests.ios.ts
@@ -37,7 +37,6 @@ export function test_WhenShowingModalPageUnloadedIsNotFiredForTheMasterPage() {
let onModalUnloaded = function (args: EventData) {
modalUnloaded++;
modalPage.off(Page.unloadedEvent, onModalUnloaded);
- TKUnit.assertNull(masterPage.modal, "currentPage.modal should be undefined when no modal page is shown!");
}
var navigatedToEventHandler = function (args) {
diff --git a/tests/app/ui/text-field/text-field-tests.ts b/tests/app/ui/text-field/text-field-tests.ts
index 1ecc4b92b..fae77c0f1 100644
--- a/tests/app/ui/text-field/text-field-tests.ts
+++ b/tests/app/ui/text-field/text-field-tests.ts
@@ -200,7 +200,7 @@ if (isIOS) {
helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) {
var textField = views[0];
textField.color = new Color("red");
- TKUnit.assertEqual(textField.color.ios.CGColor, textField.ios.textColor.CGColor, "textField.color");
+ TKUnit.assert(textField.color.ios.CGColor.isEqual(textField.ios.textColor.CGColor), "textField.color");
});
}
}
@@ -633,7 +633,7 @@ export function test_android_ime_actions_move_focus() {
if (isIOS) {
return;
}
-
+
const stack = new StackLayout();
const addTextField = () => {
const tf = new TextField();
@@ -655,7 +655,7 @@ export function test_android_ime_actions_move_focus() {
let edittext = stack._context.getCurrentFocus();
TKUnit.assertNotNull(edittext, "TextField not focused.");
-
+
edittext.onEditorAction(android.view.inputmethod.EditorInfo.IME_ACTION_NEXT);
assert(0, 1);
assert(1, 0);
@@ -691,4 +691,4 @@ export function test_android_ime_actions_move_focus() {
assert(1, 2);
assert(2, 2);
});
-}
\ No newline at end of file
+}
diff --git a/tns-core-modules/package.json b/tns-core-modules/package.json
index a33a4d315..d3fa185c8 100644
--- a/tns-core-modules/package.json
+++ b/tns-core-modules/package.json
@@ -1,7 +1,7 @@
{
"name": "tns-core-modules",
"description": "Telerik NativeScript Core Modules",
- "version": "5.0.5",
+ "version": "5.1.0",
"homepage": "https://www.nativescript.org",
"repository": {
"type": "git",
@@ -26,7 +26,7 @@
"license": "Apache-2.0",
"typings": "tns-core-modules.d.ts",
"dependencies": {
- "tns-core-modules-widgets": "5.0.1",
+ "tns-core-modules-widgets": "5.1.1",
"tslib": "^1.9.3"
},
"devDependencies": {
diff --git a/tns-core-modules/ui/action-bar/action-bar.ios.ts b/tns-core-modules/ui/action-bar/action-bar.ios.ts
index b95b57301..459a78614 100644
--- a/tns-core-modules/ui/action-bar/action-bar.ios.ts
+++ b/tns-core-modules/ui/action-bar/action-bar.ios.ts
@@ -258,18 +258,24 @@ export class ActionBar extends ActionBarBase {
private updateColors(navBar: UINavigationBar) {
const color = this.color;
- if (color) {
- navBar.titleTextAttributes = { [NSForegroundColorAttributeName]: color.ios };
- navBar.tintColor = color.ios;
- } else {
- navBar.titleTextAttributes = null;
- navBar.tintColor = null;
- }
+ this.setColor(navBar, color);
const bgColor = this.backgroundColor;
navBar.barTintColor = bgColor ? bgColor.ios : null;
}
+ private setColor(navBar: UINavigationBar, color?: Color) {
+ if (color) {
+ navBar.titleTextAttributes = { [NSForegroundColorAttributeName]: color.ios };
+ navBar.largeTitleTextAttributes = { [NSForegroundColorAttributeName]: color.ios };
+ navBar.tintColor = color.ios;
+ } else {
+ navBar.titleTextAttributes = null;
+ navBar.largeTitleTextAttributes = null;
+ navBar.tintColor = null;
+ }
+ }
+
public _onTitlePropertyChanged() {
const page = this.page;
if (!page) {
@@ -359,13 +365,7 @@ export class ActionBar extends ActionBarBase {
}
[colorProperty.setNative](color: Color) {
const navBar = this.navBar;
- if (color) {
- navBar.tintColor = color.ios;
- navBar.titleTextAttributes = { [NSForegroundColorAttributeName]: color.ios };
- } else {
- navBar.tintColor = null;
- navBar.titleTextAttributes = null;
- }
+ this.setColor(navBar, color);
}
[backgroundColorProperty.getDefault](): UIColor {
diff --git a/tns-core-modules/ui/core/view/view-common.ts b/tns-core-modules/ui/core/view/view-common.ts
index 45727dea4..c16b34372 100644
--- a/tns-core-modules/ui/core/view/view-common.ts
+++ b/tns-core-modules/ui/core/view/view-common.ts
@@ -6,7 +6,8 @@ import {
import {
ViewBase, Property, booleanConverter, EventData, layout,
- getEventOrGestureName, traceEnabled, traceWrite, traceCategories
+ getEventOrGestureName, traceEnabled, traceWrite, traceCategories,
+ InheritedProperty
} from "../view-base";
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
@@ -225,10 +226,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
const animated = arguments[4];
const stretched = arguments[5];
- const view: ViewDefinition = firstAgrument instanceof ViewCommon
- ? firstAgrument : createViewFromEntry({ moduleName: firstAgrument });
+ const view = firstAgrument instanceof ViewCommon
+ ? firstAgrument : createViewFromEntry({ moduleName: firstAgrument });
- (view)._showNativeModalView(this, context, closeCallback, fullscreen, animated, stretched);
+ view._showNativeModalView(this, context, closeCallback, fullscreen, animated, stretched);
return view;
}
}
@@ -256,27 +257,28 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
this._modalParent = parent;
this._modalContext = context;
const that = this;
- this._closeModalCallback = function () {
+ this._closeModalCallback = function (...originalArgs) {
if (that._closeModalCallback) {
const modalIndex = _rootModalViews.indexOf(that);
_rootModalViews.splice(modalIndex);
- that._hideNativeModalView(parent);
that._modalParent = null;
that._modalContext = null;
that._closeModalCallback = null;
that._dialogClosed();
parent._modal = null;
- if (typeof closeCallback === "function") {
- closeCallback.apply(undefined, arguments);
+ const whenClosedCallback = () => {
+ if (typeof closeCallback === "function") {
+ closeCallback.apply(undefined, originalArgs);
+ }
}
+
+ that._hideNativeModalView(parent, whenClosedCallback);
}
};
}
- protected _hideNativeModalView(parent: ViewCommon) {
- //
- }
+ protected abstract _hideNativeModalView(parent: ViewCommon, whenClosedCallback: () => void);
protected _raiseLayoutChangedEvent() {
const args: EventData = {
@@ -587,6 +589,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public isEnabled: boolean;
public isUserInteractionEnabled: boolean;
public iosOverflowSafeArea: boolean;
+ public iosOverflowSafeAreaEnabled: boolean;
get isLayoutValid(): boolean {
return this._isLayoutValid;
@@ -1030,3 +1033,6 @@ isUserInteractionEnabledProperty.register(ViewCommon);
export const iosOverflowSafeAreaProperty = new Property({ name: "iosOverflowSafeArea", defaultValue: false, valueConverter: booleanConverter });
iosOverflowSafeAreaProperty.register(ViewCommon);
+
+export const iosOverflowSafeAreaEnabledProperty = new InheritedProperty({ name: "iosOverflowSafeAreaEnabled", defaultValue: true, valueConverter: booleanConverter });
+iosOverflowSafeAreaEnabledProperty.register(ViewCommon);
diff --git a/tns-core-modules/ui/core/view/view.android.ts b/tns-core-modules/ui/core/view/view.android.ts
index 8d97d79c9..a75bfc731 100644
--- a/tns-core-modules/ui/core/view/view.android.ts
+++ b/tns-core-modules/ui/core/view/view.android.ts
@@ -31,7 +31,6 @@ const androidBackPressedEvent = "androidBackPressed";
const modalMap = new Map();
let TouchListener: TouchListener;
-let disableUserInteractionListener: org.nativescript.widgets.DisableUserInteractionListener;
let DialogFragment: DialogFragment;
interface DialogOptions {
@@ -50,14 +49,6 @@ interface DialogFragment {
new(): android.support.v4.app.DialogFragment;
}
-function initializeDisabledListener(): void {
- if (disableUserInteractionListener) {
- return;
- }
-
- disableUserInteractionListener = new org.nativescript.widgets.DisableUserInteractionListener();
-}
-
function initializeTouchListener(): void {
if (TouchListener) {
return;
@@ -613,14 +604,14 @@ export class View extends ViewCommon {
this._dialogFragment.show(parent._getRootFragmentManager(), this._domId.toString());
}
- protected _hideNativeModalView(parent: View) {
+ protected _hideNativeModalView(parent: View, whenClosedCallback: () => void) {
const manager = this._dialogFragment.getFragmentManager();
if (manager) {
this._dialogFragment.dismissAllowingStateLoss();
}
this._dialogFragment = null;
- super._hideNativeModalView(parent);
+ whenClosedCallback();
}
[isEnabledProperty.setNative](value: boolean) {
@@ -649,9 +640,8 @@ export class View extends ViewCommon {
}
[isUserInteractionEnabledProperty.setNative](value: boolean) {
- if (this.nativeViewProtected.setClickable) {
- this.nativeViewProtected.setClickable(value);
- }
+ this.nativeViewProtected.setClickable(value);
+ this.nativeViewProtected.setFocusable(value);
}
[visibilityProperty.getDefault](): Visibility {
diff --git a/tns-core-modules/ui/core/view/view.d.ts b/tns-core-modules/ui/core/view/view.d.ts
index 83efcd184..d638efc1e 100644
--- a/tns-core-modules/ui/core/view/view.d.ts
+++ b/tns-core-modules/ui/core/view/view.d.ts
@@ -3,7 +3,7 @@
*/ /** */
/// Include global typings
-import { ViewBase, Property, EventData, Color } from "../view-base";
+import { ViewBase, Property, InheritedProperty, EventData, Color } from "../view-base";
import { Animation, AnimationDefinition, AnimationPromise } from "../../animation";
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
import { GestureTypes, GestureEventData, GesturesObserver } from "../../gestures";
@@ -349,6 +349,11 @@ export abstract class View extends ViewBase {
*/
iosOverflowSafeArea: boolean;
+ /**
+ * Enables or disables the iosOverflowSafeArea property for all children. This property is iOS specific. Default value: true
+ */
+ iosOverflowSafeAreaEnabled: boolean;
+
/**
* Gets is layout is valid. This is a read-only property.
*/
@@ -797,6 +802,7 @@ export const originYProperty: Property;
export const isEnabledProperty: Property;
export const isUserInteractionEnabledProperty: Property;
export const iosOverflowSafeAreaProperty: Property;
+export const iosOverflowSafeAreaEnabledProperty: InheritedProperty;
export namespace ios {
/**
diff --git a/tns-core-modules/ui/core/view/view.ios.ts b/tns-core-modules/ui/core/view/view.ios.ts
index 499061701..be5374f77 100644
--- a/tns-core-modules/ui/core/view/view.ios.ts
+++ b/tns-core-modules/ui/core/view/view.ios.ts
@@ -243,7 +243,7 @@ export class View extends ViewCommon {
return null;
}
- if (!this.iosOverflowSafeArea) {
+ if (!this.iosOverflowSafeArea || !this.iosOverflowSafeAreaEnabled) {
return ios.shrinkToSafeArea(this, frame);
} else if (this.nativeViewProtected && this.nativeViewProtected.window) {
return ios.expandBeyondSafeArea(this, frame);
@@ -380,8 +380,14 @@ export class View extends ViewCommon {
}
const parentController = parentWithController.viewController;
+ if (parentController.presentedViewController) {
+ traceWrite("Parent is already presenting view controller. Close the current modal page before showing another one!",
+ traceCategories.ViewHierarchy, traceMessageType.error);
+ return;
+ }
+
if (!parentController.view || !parentController.view.window) {
- traceWrite("Parent page is not part of the window hierarchy. Close the current modal page before showing another one!",
+ traceWrite("Parent page is not part of the window hierarchy.",
traceCategories.ViewHierarchy, traceMessageType.error);
return;
}
@@ -426,7 +432,7 @@ export class View extends ViewCommon {
}
}
- protected _hideNativeModalView(parent: View) {
+ protected _hideNativeModalView(parent: View, whenClosedCallback: () => void) {
if (!parent || !parent.viewController) {
traceError("Trying to hide modal view but no parent with viewController specified.")
return;
@@ -435,8 +441,7 @@ export class View extends ViewCommon {
const parentController = parent.viewController;
const animated = (this.viewController).animated;
- super._hideNativeModalView(parent);
- parentController.dismissModalViewControllerAnimated(animated);
+ parentController.dismissViewControllerAnimatedCompletion(animated, whenClosedCallback);
}
[isEnabledProperty.getDefault](): boolean {
@@ -907,11 +912,11 @@ export namespace ios {
const parentPageInsetsTop = parent.nativeViewProtected.safeAreaInsets.top;
const currentInsetsTop = this.view.safeAreaInsets.top;
const additionalInsetsTop = Math.max(parentPageInsetsTop - currentInsetsTop, 0);
-
+
const parentPageInsetsBottom = parent.nativeViewProtected.safeAreaInsets.bottom;
const currentInsetsBottom = this.view.safeAreaInsets.bottom;
const additionalInsetsBottom = Math.max(parentPageInsetsBottom - currentInsetsBottom, 0);
-
+
if (additionalInsetsTop > 0 || additionalInsetsBottom > 0) {
const additionalInsets = new UIEdgeInsets({ top: additionalInsetsTop, left: 0, bottom: additionalInsetsBottom, right: 0 });
this.additionalSafeAreaInsets = additionalInsets;
diff --git a/tns-core-modules/ui/dialogs/dialogs-common.ts b/tns-core-modules/ui/dialogs/dialogs-common.ts
index 84847036a..f654efabd 100644
--- a/tns-core-modules/ui/dialogs/dialogs-common.ts
+++ b/tns-core-modules/ui/dialogs/dialogs-common.ts
@@ -4,6 +4,8 @@ import { Color } from "../../color";
import { Page } from "../page";
import { isIOS } from "../../platform";
import * as frameModule from "../frame";
+import { LoginOptions } from "./dialogs";
+import { isObject, isString } from "../../utils/types";
export const STRING = "string";
export const PROMPT = "Prompt";
@@ -152,3 +154,34 @@ export function getTextFieldColor(): Color {
export function isDialogOptions(arg): boolean {
return arg && (arg.message || arg.title);
}
+
+export function parseLoginOptions(args: any[]): LoginOptions {
+ // Handle options object first
+ if (args.length === 1 && isObject(args[0])) {
+ return args[0];
+ }
+
+ let options: LoginOptions = { title: LOGIN, okButtonText: OK, cancelButtonText: CANCEL };
+
+ if (isString(args[0])) {
+ options.message = args[0];
+ }
+
+ if (isString(args[1])) {
+ options.userNameHint = args[1];
+ }
+
+ if (isString(args[2])) {
+ options.passwordHint = args[2];
+ }
+
+ if (isString(args[3])) {
+ options.userName = args[3];
+ }
+
+ if (isString(args[4])) {
+ options.password = args[4];
+ }
+
+ return options;
+}
diff --git a/tns-core-modules/ui/dialogs/dialogs.android.ts b/tns-core-modules/ui/dialogs/dialogs.android.ts
index adf18020d..a9038e57f 100644
--- a/tns-core-modules/ui/dialogs/dialogs.android.ts
+++ b/tns-core-modules/ui/dialogs/dialogs.android.ts
@@ -2,7 +2,7 @@
* Android specific dialogs functions implementation.
*/
import { DialogOptions, ConfirmOptions, PromptOptions, PromptResult, LoginOptions, LoginResult, ActionOptions } from ".";
-import { getLabelColor, getButtonColors, isDialogOptions, inputType, capitalizationType, ALERT, OK, CONFIRM, CANCEL, PROMPT, LOGIN } from "./dialogs-common";
+import { getLabelColor, getButtonColors, isDialogOptions, inputType, capitalizationType, ALERT, OK, CONFIRM, CANCEL, PROMPT, parseLoginOptions } from "./dialogs-common";
import { android as androidApp } from "../../application";
export * from "./dialogs-common";
@@ -223,31 +223,8 @@ export function prompt(arg: any): Promise {
});
}
-export function login(arg: any): Promise {
- let options: LoginOptions;
- const defaultOptions = { title: LOGIN, okButtonText: OK, cancelButtonText: CANCEL };
-
- if (arguments.length === 1) {
- if (isString(arguments[0])) {
- options = defaultOptions;
- options.message = arguments[0];
- } else {
- options = arguments[0];
- }
- } else if (arguments.length === 2) {
- if (isString(arguments[0]) && isString(arguments[1])) {
- options = defaultOptions;
- options.message = arguments[0];
- options.userName = arguments[1];
- }
- } else if (arguments.length === 3) {
- if (isString(arguments[0]) && isString(arguments[1]) && isString(arguments[2])) {
- options = defaultOptions;
- options.message = arguments[0];
- options.userName = arguments[1];
- options.password = arguments[2];
- }
- }
+export function login(...args: any[]): Promise {
+ let options: LoginOptions = parseLoginOptions(args);
return new Promise((resolve, reject) => {
try {
@@ -256,10 +233,15 @@ export function login(arg: any): Promise {
const alert = createAlertDialog(options);
const userNameInput = new android.widget.EditText(context);
+
+ userNameInput.setHint(options.userNameHint ? options.userNameHint : "");
userNameInput.setText(options.userName ? options.userName : "");
const passwordInput = new android.widget.EditText(context);
passwordInput.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ passwordInput.setTypeface(android.graphics.Typeface.DEFAULT);
+
+ passwordInput.setHint(options.userNameHint ? options.userNameHint : "");
passwordInput.setText(options.password ? options.password : "");
const layout = new android.widget.LinearLayout(context);
@@ -278,11 +260,9 @@ export function login(arg: any): Promise {
});
showDialog(alert);
-
} catch (ex) {
reject(ex);
}
-
});
}
diff --git a/tns-core-modules/ui/dialogs/dialogs.d.ts b/tns-core-modules/ui/dialogs/dialogs.d.ts
index 0c2a68521..5c0a80c43 100644
--- a/tns-core-modules/ui/dialogs/dialogs.d.ts
+++ b/tns-core-modules/ui/dialogs/dialogs.d.ts
@@ -98,10 +98,20 @@ export function prompt(options: PromptOptions): Promise;
/**
* The login() method displays a login dialog box that prompts the visitor for user name and password.
* @param message The text to display in the dialog box.
+ * @param userNameHint The default text to display as a hint in the username input. Optional.
+ * @param passwordHint The default text to display as a hint in the password input. Optional.
* @param userName The default text to display in the user name input box. Optional.
* @param password The default text to display in the password input box. Optional.
*/
-export function login(message: string, userName?: string, password?: string): Promise;
+export function login(message: string, userNameHint?: string, passwordHint?: string, userName?: string, password?: string): Promise;
+
+/**
+ * The login() method displays a login dialog box that prompts the visitor for user name and password.
+ * @param message The text to display in the dialog box.
+ * @param userNameHint The default text to display as a hint in the username input. Optional.
+ * @param passwordHint The default text to display as a hint in the password input. Optional.
+ */
+export function login(message: string, userNameHint?: string, passwordHint?: string): Promise;
/**
* The login() method displays a login dialog box that prompts the visitor for user name and password.
@@ -223,6 +233,16 @@ export interface PromptOptions extends ConfirmOptions {
* Provides options for the login dialog.
*/
export interface LoginOptions extends ConfirmOptions {
+ /**
+ * Gets or sets the default text to display as hint in the user name input box.
+ */
+ userNameHint?: string;
+
+ /**
+ * Gets or sets the default text to display as hint in the password input box.
+ */
+ passwordHint?: string;
+
/**
* Gets or sets the default text to display in the user name input box.
*/
diff --git a/tns-core-modules/ui/dialogs/dialogs.ios.ts b/tns-core-modules/ui/dialogs/dialogs.ios.ts
index 39e9bad00..598f49389 100644
--- a/tns-core-modules/ui/dialogs/dialogs.ios.ts
+++ b/tns-core-modules/ui/dialogs/dialogs.ios.ts
@@ -3,7 +3,7 @@
*/
import { View, ios as iosView } from "../core/view";
import { ConfirmOptions, PromptOptions, PromptResult, LoginOptions, LoginResult, ActionOptions } from ".";
-import { getCurrentPage, getLabelColor, getButtonColors, getTextFieldColor, isDialogOptions, inputType, capitalizationType, ALERT, OK, CONFIRM, CANCEL, PROMPT, LOGIN } from "./dialogs-common";
+import { getCurrentPage, getLabelColor, getButtonColors, getTextFieldColor, isDialogOptions, inputType, capitalizationType, ALERT, OK, CONFIRM, CANCEL, PROMPT, parseLoginOptions } from "./dialogs-common";
import { isString, isDefined, isFunction } from "../../utils/types";
import { getRootView } from "../../application";
@@ -145,32 +145,8 @@ export function prompt(arg: any): Promise {
});
}
-export function login(): Promise {
- let options: LoginOptions;
-
- let defaultOptions = { title: LOGIN, okButtonText: OK, cancelButtonText: CANCEL };
-
- if (arguments.length === 1) {
- if (isString(arguments[0])) {
- options = defaultOptions;
- options.message = arguments[0];
- } else {
- options = arguments[0];
- }
- } else if (arguments.length === 2) {
- if (isString(arguments[0]) && isString(arguments[1])) {
- options = defaultOptions;
- options.message = arguments[0];
- options.userName = arguments[1];
- }
- } else if (arguments.length === 3) {
- if (isString(arguments[0]) && isString(arguments[1]) && isString(arguments[2])) {
- options = defaultOptions;
- options.message = arguments[0];
- options.userName = arguments[1];
- options.password = arguments[2];
- }
- }
+export function login(...args: any[]): Promise {
+ let options: LoginOptions = parseLoginOptions(args);
return new Promise((resolve, reject) => {
try {
@@ -182,6 +158,7 @@ export function login(): Promise {
alertController.addTextFieldWithConfigurationHandler((arg: UITextField) => {
arg.placeholder = "Login";
+ arg.placeholder = options.userNameHint ? options.userNameHint : "";
arg.text = isString(options.userName) ? options.userName : "";
if (textFieldColor) {
@@ -192,6 +169,7 @@ export function login(): Promise {
alertController.addTextFieldWithConfigurationHandler((arg: UITextField) => {
arg.placeholder = "Password";
arg.secureTextEntry = true;
+ arg.placeholder = options.passwordHint ? options.passwordHint : "";
arg.text = isString(options.password) ? options.password : "";
if (textFieldColor) {
diff --git a/tns-core-modules/ui/enums/enums.d.ts b/tns-core-modules/ui/enums/enums.d.ts
index 8959367c6..d54ac27d9 100644
--- a/tns-core-modules/ui/enums/enums.d.ts
+++ b/tns-core-modules/ui/enums/enums.d.ts
@@ -3,6 +3,43 @@
*/ /** */
import * as animationModule from "../animation";
+import {
+ KeyboardType as BaseKeyboardType,
+ ReturnKeyType as BaseReturnKeyType,
+ UpdateTextTrigger as BaseUpdateTrigger,
+ AutocapitalizationType as BaseAutocapitalizationType
+} from "../editable-text-base";
+
+import {
+ WhiteSpace as BaseWhiteSpace,
+ TextAlignment as BaseTextAlignment,
+ TextTransform as BaseTextTransform,
+ TextDecoration as BaseTextDecoration
+} from "../text-base";
+
+import {
+ Orientation as BaseOrientation
+} from "../layouts/stack-layout";
+
+import {
+ Dock as BaseDock
+} from "../layouts/dock-layout";
+
+import {
+ BackgroundRepeat as BaseBackgroundRepeat,
+ Visibility as BaseVisibility,
+ HorizontalAlignment as BaseHorizontalAlignment,
+ VerticalAlignment as BaseVerticalAlignment
+} from "../styling/style-properties";
+
+import {
+ Stretch as BaseStretch
+} from "../image";
+
+import {
+ FontStyle as BaseFontStyle,
+ FontWeight as BaseFontWeight
+} from "../styling/font-common";
/**
* Represents a soft keyboard flavor.
@@ -12,31 +49,30 @@ export module KeyboardType {
* Android: [TYPE_CLASS_DATETIME](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_DATETIME) | [TYPE_DATETIME_VARIATION_NORMAL](http://developer.android.com/reference/android/text/InputType.html#TYPE_DATETIME_VARIATION_NORMAL)
* iOS: [UIKeyboardTypeNumbersAndPunctuation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
- export var datetime: string;
-
+ export var datetime: BaseKeyboardType
/**
* Android: [TYPE_CLASS_PHONE](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_PHONE)
* iOS: [UIKeyboardTypePhonePad](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
- export var phone: string;
+ export var phone: BaseKeyboardType
/**
* Android: [TYPE_CLASS_NUMBER](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_NUMBER) | [TYPE_NUMBER_VARIATION_NORMAL](http://developer.android.com/intl/es/reference/android/text/InputType.html#TYPE_NUMBER_VARIATION_NORMAL) | [TYPE_NUMBER_FLAG_SIGNED](http://developer.android.com/reference/android/text/InputType.html#TYPE_NUMBER_FLAG_SIGNED) | [TYPE_NUMBER_FLAG_DECIMAL](http://developer.android.com/reference/android/text/InputType.html#TYPE_NUMBER_FLAG_DECIMAL)
* iOS: [UIKeyboardTypeNumbersAndPunctuation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
- export var number: string;
+ export var number: BaseKeyboardType
/**
* Android: [TYPE_CLASS_TEXT](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_TEXT) | [TYPE_TEXT_VARIATION_URI](http://developer.android.com/reference/android/text/InputType.html#TYPE_TEXT_VARIATION_URI)
* iOS: [UIKeyboardTypeURL](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
- export var url: string;
+ export var url: BaseKeyboardType
/**
* Android: [TYPE_CLASS_TEXT](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_TEXT) | [TYPE_TEXT_VARIATION_EMAIL_ADDRESS](http://developer.android.com/reference/android/text/InputType.html#TYPE_TEXT_VARIATION_EMAIL_ADDRESS)
* iOS: [UIKeyboardTypeEmailAddress](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType)
*/
- export var email: string;
+ export var email: BaseKeyboardType
}
/**
@@ -47,25 +83,25 @@ export module ReturnKeyType {
* Android: [IME_ACTION_DONE](http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html#IME_ACTION_DONE)
* iOS: [UIReturnKeyDone](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIReturnKeyType)
*/
- export var done: string;
+ export var done: BaseReturnKeyType;
/**
* Android: [IME_ACTION_NEXT](http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html#IME_ACTION_NEXT)
* iOS: [UIReturnKeyNext](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIReturnKeyType)
*/
- export var next: string;
+ export var next: BaseReturnKeyType;
/**
* Android: [IME_ACTION_GO](http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html#IME_ACTION_GO)
* iOS: [UIReturnKeyGo](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIReturnKeyType)
*/
- export var go: string;
+ export var go: BaseReturnKeyType;
/**
* Android: [IME_ACTION_SEARCH](http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html#IME_ACTION_SEARCH)
* iOS: [UIReturnKeySearch](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIReturnKeyType)
*/
- export var search: string;
+ export var search: BaseReturnKeyType;
/**
* Android: [IME_ACTION_SEND](http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html#IME_ACTION_SEND)
@@ -81,17 +117,17 @@ export module TextAlignment {
/**
* Represents left text-align.
*/
- export var left: string;
+ export var left: BaseTextAlignment;
/**
* Represents center text-align.
*/
- export var center: string;
+ export var center: BaseTextAlignment;
/**
* Represents right text-align.
*/
- export var right: string;
+ export var right: BaseTextAlignment;
}
/**
@@ -101,11 +137,11 @@ export module Orientation {
/**
* Layout should be horizontally oriented.
*/
- export var horizontal: string;
+ export var horizontal: BaseOrientation;
/**
* Layout should be vertically oriented.
*/
- export var vertical: string;
+ export var vertical: BaseOrientation;
}
/**
@@ -133,22 +169,22 @@ export module HorizontalAlignment {
/**
* An element should be left aligned.
*/
- export var left: string;
+ export var left: BaseHorizontalAlignment;
/**
* An element should be center aligned.
*/
- export var center: string;
+ export var center: BaseHorizontalAlignment;
/**
* An element should be right aligned.
*/
- export var right: string;
+ export var right: BaseHorizontalAlignment;
/**
* An element should be stretched to fill all the available size.
*/
- export var stretch: string;
+ export var stretch: BaseHorizontalAlignment;
}
/**
@@ -158,27 +194,27 @@ export module VerticalAlignment {
/**
* An element should be top aligned.
*/
- export var top: string;
+ export var top: BaseVerticalAlignment;
/**
* An element should be center aligned.
*/
- export var center: string;
+ export var center: BaseVerticalAlignment;
/**
* Same as center. An element should be aligned in the middle.
*/
- export var middle: string;
+ export var middle: BaseVerticalAlignment;
/**
* An element should be bottom aligned.
*/
- export var bottom: string;
+ export var bottom: BaseVerticalAlignment;
/**
* An element should be stretched to fill all the available size.
*/
- export var stretch: string;
+ export var stretch: BaseVerticalAlignment;
}
/**
@@ -188,25 +224,25 @@ export module Stretch {
/**
* The image preserves its original size.
*/
- export var none: string;
+ export var none: BaseStretch;
/**
* The image is resized to fill in the destination dimensions while it preserves its native aspect ratio.
* If the aspect ratio of the destination rectangle differs from the image, the image is clipped to fill
* in the destination.
*/
- export var aspectFill: string;
+ export var aspectFill: BaseStretch;
/**
* The image is resized to fit the destination dimensions while it preserves
* its native aspect ratio.
*/
- export var aspectFit: string;
+ export var aspectFit: BaseStretch;
/**
* The image is resized to fill the destination dimensions. The aspect ratio is not preserved.
*/
- export var fill: string;
+ export var fill: BaseStretch;
}
/**
@@ -216,17 +252,17 @@ export module Visibility {
/**
* The view is visible.
*/
- export var visible: string;
+ export var visible: BaseVisibility;
/**
* The view is not visible and won't take place in the layout.
*/
- export var collapse: string;
+ export var collapse: BaseVisibility;
/**
* The view is not visible but will take place in the layout.
*/
- export var hidden: string;
+ export var hidden: BaseVisibility;
}
/**
@@ -271,12 +307,12 @@ export module UpdateTextTrigger {
/**
* The text property will be udpaded when the widget loses focus.
*/
- export var focusLost: string;
+ export var focusLost: BaseUpdateTrigger;
/**
* The text property will be udpaded on every single character typed by the user.
*/
- export var textChanged: string;
+ export var textChanged: BaseUpdateTrigger;
}
/**
@@ -301,22 +337,22 @@ export module Dock {
/**
* A child element that is positioned on the left side of the DockLayout.
*/
- export var left: string;
+ export var left: BaseDock;
/**
* A child element that is positioned on the top side of the DockLayout.
*/
- export var top: string;
+ export var top: BaseDock;
/**
* A child element that is positioned on the right side of the DockLayout.
*/
- export var right: string;
+ export var right: BaseDock;
/**
* A child element that is positioned on the bottom side of the DockLayout.
*/
- export var bottom: string;
+ export var bottom: BaseDock;
}
/**
@@ -326,22 +362,22 @@ export module AutocapitalizationType {
/**
* Do not capitalize any text automatically.
*/
- export var none: string;
+ export var none: BaseAutocapitalizationType;
/**
* Capitalize the first letter of each word automatically.
*/
- export var words: string;
+ export var words: BaseAutocapitalizationType;
/**
* Capitalize the first letter of each sentence automatically.
*/
- export var sentences: string;
+ export var sentences: BaseAutocapitalizationType;
/**
* Capitalize all characters automatically.
*/
- export var allCharacters: string;
+ export var allCharacters: BaseAutocapitalizationType;
}
/**
@@ -420,12 +456,12 @@ export module FontStyle {
/**
* Normal font style.
*/
- export var normal: string;
+ export var normal: BaseFontStyle;
/**
* Italic font style.
*/
- export var italic: string;
+ export var italic: BaseFontStyle;
}
/**
@@ -435,17 +471,17 @@ export module TextDecoration {
/**
* No decoration.
*/
- export var none: string;
+ export var none: BaseTextDecoration;
/**
* Text decoration underline.
*/
- export var underline: string;
+ export var underline: BaseTextDecoration;
/**
* Text decoration line-through.
*/
- export var lineThrough: string;
+ export var lineThrough: BaseTextDecoration;
}
/**
@@ -455,22 +491,22 @@ export module TextTransform {
/**
* No transform.
*/
- export var none: string;
+ export var none: BaseTextTransform;
/**
* Text transform capitalize.
*/
- export var capitalize: string;
+ export var capitalize: BaseTextTransform;
/**
* Text transform uppercase.
*/
- export var uppercase: string;
+ export var uppercase: BaseTextTransform;
/**
* Text transform lowercase.
*/
- export var lowercase: string;
+ export var lowercase: BaseTextTransform;
}
/**
@@ -480,12 +516,12 @@ export module WhiteSpace {
/**
* Normal wrap.
*/
- export var normal: string;
+ export var normal: BaseWhiteSpace;
/**
* No wrap.
*/
- export var nowrap: string;
+ export var nowrap: BaseWhiteSpace;
}
/**
@@ -495,57 +531,57 @@ export module FontWeight {
/**
* Thin font weight. CSS font-weight 100.
*/
- export var thin: string;
+ export var thin: BaseFontWeight;
/**
* Extra-light / Ultra-light font weight. CSS font-weight 200.
*/
- export var extraLight: string;
+ export var extraLight: BaseFontWeight;
/**
* Light font weight. CSS font-weight 300.
*/
- export var light: string;
+ export var light: BaseFontWeight;
/**
* Normal font weight. CSS font-weight 400.
*/
- export var normal: string;
+ export var normal: BaseFontWeight;
/**
* Medium font weight. CSS font-weight 500.
*/
- export var medium: string;
+ export var medium: BaseFontWeight;
/**
* Semi-bold / Demi-bold font weight. CSS font-weight 600.
*/
- export var semiBold: string;
+ export var semiBold: BaseFontWeight;
/**
* Bold font weight. CSS font-weight 700.
*/
- export var bold: string;
+ export var bold: BaseFontWeight;
/**
* Extra-bold / Ultra-bold font weight. CSS font-weight 800.
*/
- export var extraBold: string;
+ export var extraBold: BaseFontWeight;
/**
* Black font weight. CSS font-weight 900.
*/
- export var black: string;
+ export var black: BaseFontWeight;
}
/**
* Specifies background repeat.
*/
export module BackgroundRepeat {
- export var repeat: string;
- export var repeatX: string;
- export var repeatY: string;
- export var noRepeat: string;
+ export var repeat: BaseBackgroundRepeat;
+ export var repeatX: BaseBackgroundRepeat;
+ export var repeatY: BaseBackgroundRepeat;
+ export var noRepeat: BaseBackgroundRepeat;
}
/**
diff --git a/tns-core-modules/ui/list-view/list-view.ios.ts b/tns-core-modules/ui/list-view/list-view.ios.ts
index 896e076fd..da0b0cb2c 100644
--- a/tns-core-modules/ui/list-view/list-view.ios.ts
+++ b/tns-core-modules/ui/list-view/list-view.ios.ts
@@ -95,6 +95,7 @@ class DataSource extends NSObject implements UITableViewDataSource {
let width = layout.getMeasureSpecSize(owner.widthMeasureSpec);
let rowHeight = owner._effectiveRowHeight;
let cellHeight = rowHeight > 0 ? rowHeight : owner.getHeight(indexPath.row);
+ cellView.iosOverflowSafeAreaEnabled = false;
View.layoutChild(owner, cellView, 0, 0, width, cellHeight);
}
}
@@ -389,6 +390,7 @@ export class ListView extends ListViewBase {
let cellHeight = rowHeight > 0 ? rowHeight : this.getHeight(childView._listViewItemIndex);
if (cellHeight) {
let width = layout.getMeasureSpecSize(this.widthMeasureSpec);
+ childView.iosOverflowSafeAreaEnabled = false;
View.layoutChild(this, childView, 0, 0, width, cellHeight);
}
});
diff --git a/tns-core-modules/ui/scroll-view/scroll-view-common.ts b/tns-core-modules/ui/scroll-view/scroll-view-common.ts
index fef327b48..8dbf4b5a6 100644
--- a/tns-core-modules/ui/scroll-view/scroll-view-common.ts
+++ b/tns-core-modules/ui/scroll-view/scroll-view-common.ts
@@ -11,6 +11,7 @@ export abstract class ScrollViewBase extends ContentView implements ScrollViewDe
public orientation: Orientation;
public scrollBarIndicatorVisible: boolean;
+ public isScrollEnabled: boolean;
public addEventListener(arg: string, callback: any, thisArg?: any) {
super.addEventListener(arg, callback, thisArg);
@@ -102,4 +103,10 @@ export const scrollBarIndicatorVisibleProperty = new Property({
+ name: "isScrollEnabled", defaultValue: true,
+ valueConverter: booleanConverter
+});
+isScrollEnabledProperty.register(ScrollViewBase);
\ No newline at end of file
diff --git a/tns-core-modules/ui/scroll-view/scroll-view.android.ts b/tns-core-modules/ui/scroll-view/scroll-view.android.ts
index f2f33e0e7..58660d9a7 100644
--- a/tns-core-modules/ui/scroll-view/scroll-view.android.ts
+++ b/tns-core-modules/ui/scroll-view/scroll-view.android.ts
@@ -1,5 +1,8 @@
import { ScrollEventData } from ".";
-import { ScrollViewBase, layout, scrollBarIndicatorVisibleProperty } from "./scroll-view-common";
+import {
+ ScrollViewBase, layout, scrollBarIndicatorVisibleProperty,
+ isUserInteractionEnabledProperty, isScrollEnabledProperty
+} from "./scroll-view-common";
export * from "./scroll-view-common";
@@ -44,6 +47,21 @@ export class ScrollView extends ScrollViewBase {
return nativeView.getScrollableLength() / layout.getDisplayDensity();
}
+ [isUserInteractionEnabledProperty.setNative](value: boolean) {
+ // NOTE: different behavior on iOS & Android:
+ // iOS disables user interaction recursively for all subviews as well
+ this.nativeViewProtected.setClickable(value);
+ this.nativeViewProtected.setFocusable(value);
+ this.nativeViewProtected.setScrollEnabled(value);
+ }
+
+ [isScrollEnabledProperty.getDefault](): boolean {
+ return this.nativeViewProtected.getScrollEnabled();
+ }
+ [isScrollEnabledProperty.setNative](value: boolean) {
+ this.nativeViewProtected.setScrollEnabled(value);
+ }
+
[scrollBarIndicatorVisibleProperty.getDefault](): boolean {
return true;
}
@@ -57,7 +75,7 @@ export class ScrollView extends ScrollViewBase {
public scrollToVerticalOffset(value: number, animated: boolean) {
const nativeView = this.nativeViewProtected;
- if (nativeView && this.orientation === "vertical") {
+ if (nativeView && this.orientation === "vertical" && this.isScrollEnabled) {
value *= layout.getDisplayDensity();
if (animated) {
@@ -70,7 +88,7 @@ export class ScrollView extends ScrollViewBase {
public scrollToHorizontalOffset(value: number, animated: boolean) {
const nativeView = this.nativeViewProtected;
- if (nativeView && this.orientation === "horizontal") {
+ if (nativeView && this.orientation === "horizontal" && this.isScrollEnabled) {
value *= layout.getDisplayDensity();
if (animated) {
diff --git a/tns-core-modules/ui/scroll-view/scroll-view.d.ts b/tns-core-modules/ui/scroll-view/scroll-view.d.ts
index 0a4c73853..d9f73d137 100644
--- a/tns-core-modules/ui/scroll-view/scroll-view.d.ts
+++ b/tns-core-modules/ui/scroll-view/scroll-view.d.ts
@@ -14,6 +14,11 @@ export class ScrollView extends ContentView {
*/
public static scrollEvent: string;
+ /**
+ * Gets or sets a value indicating whether scroll is enabled.
+ */
+ isScrollEnabled: boolean;
+
/**
* Gets a value that contains the vertical offset of the scrolled content.
*/
diff --git a/tns-core-modules/ui/scroll-view/scroll-view.ios.ts b/tns-core-modules/ui/scroll-view/scroll-view.ios.ts
index f99e8b635..eeade71b8 100644
--- a/tns-core-modules/ui/scroll-view/scroll-view.ios.ts
+++ b/tns-core-modules/ui/scroll-view/scroll-view.ios.ts
@@ -1,5 +1,7 @@
import { ScrollEventData } from ".";
-import { View, layout, ScrollViewBase, scrollBarIndicatorVisibleProperty } from "./scroll-view-common";
+import {
+ View, layout, ScrollViewBase, scrollBarIndicatorVisibleProperty, isScrollEnabledProperty
+} from "./scroll-view-common";
import { ios as iosUtils } from "../../utils/utils";
// HACK: Webpack. Use a fully-qualified import to allow resolve.extensions(.ios.js) to
// kick in. `../utils` doesn't seem to trigger the webpack extensions mechanism.
@@ -99,6 +101,13 @@ export class ScrollView extends ScrollViewBase {
return Math.max(0, this.nativeViewProtected.contentSize.height - this.nativeViewProtected.bounds.size.height);
}
+ [isScrollEnabledProperty.getDefault](): boolean {
+ return this.nativeViewProtected.scrollEnabled;
+ }
+ [isScrollEnabledProperty.setNative](value: boolean) {
+ this.nativeViewProtected.scrollEnabled = value;
+ }
+
[scrollBarIndicatorVisibleProperty.getDefault](): boolean {
return true;
}
@@ -107,14 +116,14 @@ export class ScrollView extends ScrollViewBase {
}
public scrollToVerticalOffset(value: number, animated: boolean) {
- if (this.nativeViewProtected && this.orientation === "vertical") {
+ if (this.nativeViewProtected && this.orientation === "vertical" && this.isScrollEnabled) {
const bounds = this.nativeViewProtected.bounds.size;
this.nativeViewProtected.scrollRectToVisibleAnimated(CGRectMake(0, value, bounds.width, bounds.height), animated);
}
}
public scrollToHorizontalOffset(value: number, animated: boolean) {
- if (this.nativeViewProtected && this.orientation === "horizontal") {
+ if (this.nativeViewProtected && this.orientation === "horizontal" && this.isScrollEnabled) {
const bounds = this.nativeViewProtected.bounds.size;
this.nativeViewProtected.scrollRectToVisibleAnimated(CGRectMake(value, 0, bounds.width, bounds.height), animated);
}
diff --git a/tns-core-modules/ui/search-bar/search-bar.android.ts b/tns-core-modules/ui/search-bar/search-bar.android.ts
index cb95c547c..c059dee02 100644
--- a/tns-core-modules/ui/search-bar/search-bar.android.ts
+++ b/tns-core-modules/ui/search-bar/search-bar.android.ts
@@ -1,7 +1,8 @@
import { Font } from "../styling/font";
import {
SearchBarBase, Color, colorProperty, backgroundColorProperty, backgroundInternalProperty, fontInternalProperty,
- textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty, fontSizeProperty
+ textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty, fontSizeProperty,
+ isEnabledProperty, isUserInteractionEnabledProperty
} from "./search-bar-common";
import { ad } from "../../utils/utils";
@@ -76,6 +77,33 @@ function initializeNativeClasses(): void {
CloseListener = CompatCloseListenerImpl;
}
+function enableSearchView(nativeView: any, value: boolean) {
+ nativeView.setEnabled(value);
+
+ if (!(nativeView instanceof android.view.ViewGroup)) {
+ return;
+ }
+
+ for (let i = 0; i < nativeView.getChildCount(); i++) {
+ let child = nativeView.getChildAt(i);
+ enableSearchView(child, value);
+ }
+}
+
+function enableUserInteractionSearchView(nativeView: any, value: boolean) {
+ nativeView.setClickable(value);
+ nativeView.setFocusable(value);
+
+ if (!(nativeView instanceof android.view.ViewGroup)) {
+ return;
+ }
+
+ for (let i = 0; i < nativeView.getChildCount(); i++) {
+ let child = nativeView.getChildAt(i);
+ enableUserInteractionSearchView(child, value);
+ }
+}
+
export class SearchBar extends SearchBarBase {
nativeViewProtected: android.support.v7.widget.SearchView;
private _searchTextView: android.widget.TextView;
@@ -122,6 +150,14 @@ export class SearchBar extends SearchBarBase {
super.disposeNativeView();
}
+ [isEnabledProperty.setNative](value: boolean) {
+ enableSearchView(this.nativeViewProtected, value);
+ }
+
+ [isUserInteractionEnabledProperty.setNative](value: boolean) {
+ enableUserInteractionSearchView(this.nativeViewProtected, value);
+ }
+
[backgroundColorProperty.getDefault](): number {
// TODO: Why do we get DrawingCacheBackgroundColor but set backgroundColor?????
const result = this.nativeViewProtected.getDrawingCacheBackgroundColor();
diff --git a/tns-core-modules/ui/search-bar/search-bar.ios.ts b/tns-core-modules/ui/search-bar/search-bar.ios.ts
index 2b85cb710..b32de5320 100644
--- a/tns-core-modules/ui/search-bar/search-bar.ios.ts
+++ b/tns-core-modules/ui/search-bar/search-bar.ios.ts
@@ -1,7 +1,7 @@
import { Font } from "../styling/font";
import {
SearchBarBase, Color, colorProperty, backgroundColorProperty, backgroundInternalProperty, fontInternalProperty,
- textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty
+ textProperty, hintProperty, textFieldHintColorProperty, textFieldBackgroundColorProperty, isEnabledProperty
} from "./search-bar-common";
import { ios as iosUtils } from "../../utils/utils";
@@ -123,6 +123,18 @@ export class SearchBar extends SearchBarBase {
return this.__placeholderLabel;
}
+ [isEnabledProperty.setNative](value: boolean) {
+ const nativeView = this.nativeViewProtected;
+ if (nativeView instanceof UIControl) {
+ nativeView.enabled = value;
+ }
+
+ const textField = this._textField;
+ if (textField) {
+ textField.enabled = value;
+ }
+ }
+
[backgroundColorProperty.getDefault](): UIColor {
return this.ios.barTintColor;
}
diff --git a/tns-core-modules/ui/styling/background.android.ts b/tns-core-modules/ui/styling/background.android.ts
index 9f3e9ed77..123357b5d 100644
--- a/tns-core-modules/ui/styling/background.android.ts
+++ b/tns-core-modules/ui/styling/background.android.ts
@@ -50,6 +50,7 @@ export module ad {
if (isSetColorFilterOnlyWidget(nativeView)
&& drawable
+ && !(drawable instanceof org.nativescript.widgets.BorderDrawable)
&& !background.hasBorderWidth()
&& !background.hasBorderRadius()
&& !background.clipPath
diff --git a/tns-core-modules/ui/tab-view/tab-view-common.ts b/tns-core-modules/ui/tab-view/tab-view-common.ts
index 53eacbad9..e2a25ce34 100644
--- a/tns-core-modules/ui/tab-view/tab-view-common.ts
+++ b/tns-core-modules/ui/tab-view/tab-view-common.ts
@@ -2,7 +2,7 @@
import {
View, ViewBase, Style, Property, CssProperty, CoercibleProperty,
Color, isIOS, AddArrayFromBuilder, AddChildFromBuilder, EventData, CSSType,
- traceWrite, traceCategories, traceMessageType
+ traceWrite, traceCategories, traceMessageType, booleanConverter
} from "../core/view";
export * from "../core/view";
@@ -95,6 +95,7 @@ export class TabViewBase extends View implements TabViewDefinition, AddChildFrom
public selectedIndex: number;
public androidOffscreenTabLimit: number;
public androidTabsPosition: "top" | "bottom";
+ public androidSwipeEnabled: boolean;
public iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate";
get androidSelectedTabHighlightColor(): Color {
@@ -252,6 +253,9 @@ androidOffscreenTabLimitProperty.register(TabViewBase);
export const androidTabsPositionProperty = new Property({ name: "androidTabsPosition", defaultValue: "top" });
androidTabsPositionProperty.register(TabViewBase);
+export const androidSwipeEnabledProperty = new Property({ name: "androidSwipeEnabled", defaultValue: true, valueConverter: booleanConverter });
+androidSwipeEnabledProperty.register(TabViewBase);
+
export const tabTextFontSizeProperty = new CssProperty