import testModule = require("../../ui-test"); import TKUnit = require("../../TKUnit"); import testRunner = require("../../testRunner"); import app = require("application"); import helper = require("../helper"); import viewModule = require("ui/core/view"); import observable = require("data/observable"); import types = require("utils/types"); import platform = require("platform"); import utils = require("utils/utils"); //  // # ListView // Using a ListView requires the ListView module. // ``` JavaScript import listViewModule = require("ui/list-view"); // ``` // Other modules which will be used in the code samples in this article: // ``` JavaScript import observableArray = require("data/observable-array"); import labelModule = require("ui/label"); // ``` // ### Binding the ListView items property to collection in the view-model. //```XML // // {%raw%}{%endraw%} // //``` // ### Attaching event handler for the ListView itemTap event. //```XML // // {%raw%}{%endraw%} // //``` //```JS // function listViewItemTap(args) { // var itemIndex = args.index; // } // exports.listViewItemTap = listViewItemTap; //``` // ### Attaching event handler for the ListView loadMoreItems event. //```XML // // {%raw%}{%endraw%} // //``` //```JS // function listViewLoadMoreItems(args) { // // Expand your collection bound to the ListView with more items here! // } // exports.listViewLoadMoreItems = listViewLoadMoreItems; //``` // ### Define the ListView itemTemplate property. //```XML // // {%raw%} // // // {%endraw%} // //``` //  var ASYNC = 0.2; var FEW_ITEMS = [0, 1, 2]; var MANY_ITEMS = new Array(100); for (var i = 0; i < 100; i++) { MANY_ITEMS.push(i); } export class ListViewTest extends testModule.UITest { public create(): listViewModule.ListView { return new listViewModule.ListView(); } public test_default_TNS_values() { // // ### Creating a ListView // ``` JavaScript var listView = new listViewModule.ListView(); // ``` // TKUnit.assertEqual(listView.isScrolling, false, "Default listView.isScrolling"); TKUnit.assert(types.isUndefined(listView.items), "Default listView.items should be undefined"); } public test_set_items_to_array_loads_all_items(done) { var listView = this.testView; var indexes = {}; // // ### Using ListView with Array // The itemLoading event is used to create the UI for each item that is shown in the ListView. // ``` JavaScript var colors = ["red", "green", "blue"]; listView.items = colors; listView.on(listViewModule.ListView.itemLoadingEvent, function (args: listViewModule.ItemEventData) { if (!args.view) { //// Create label if it is not already created. args.view = new labelModule.Label(); } (args.view).text = colors[args.index]; // indexes[args.index] = true; if (args.index === (colors.length - 1)) { try { if (app.android) { TKUnit.assert(listView.android instanceof android.widget.ListView, "android property is android.widget.ListView"); } else if (app.ios) { TKUnit.assert(listView.ios instanceof UITableView, "ios property is UITableView"); } TKUnit.assert(indexes[0], "itemLoading not called for index 0"); TKUnit.assert(indexes[1], "itemLoading not called for index 1"); TKUnit.assert(indexes[2], "itemLoading not called for index 2"); done(null); } catch (e) { done(e); } } // }); // ``` // } public test_set_native_item_exposed() { let listView = this.testView; let indexes = {}; let colors = ["red", "green", "blue"]; listView.items = colors; listView.on(listViewModule.ListView.itemLoadingEvent, function (args: listViewModule.ItemEventData) { if (platform.device.os === platform.platformNames.ios) { indexes[args.index] = args.ios; } else if (platform.device.os === platform.platformNames.android) { indexes[args.index] = args.android; } }); TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); for (var item in indexes) { if (platform.device.os === platform.platformNames.ios) { TKUnit.assert(indexes[item] instanceof UITableViewCell, "itemLoading not called for index " + item); } else if (platform.device.os === platform.platformNames.android) { TKUnit.assert(indexes[item] instanceof android.view.ViewGroup, "itemLoading not called for index " + item); } } } public test_set_items_to_array_creates_native_views() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); listView.items = FEW_ITEMS; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), FEW_ITEMS.length, "Native views count."); } public test_refresh_after_adding_items_to_array_loads_new_items() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); var colors = ["red", "green", "blue"]; listView.items = colors; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), colors.length, "Native views count."); // // > Note, that changing the array after the list view is shown will not update the UI. // You can force-update the UI using the refresh() method. // ``` JavaScript colors.push("yellow"); //// Manually trigger the update so that the new color is shown. listView.refresh(); // ``` // TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), colors.length, "Native views count."); } public test_refresh_reloads_all_items() { let listView = this.testView; let indexes = {}; let completed = false; listView.items = FEW_ITEMS; listView.on(listViewModule.ListView.itemLoadingEvent, function (args: listViewModule.ItemEventData) { if (!args.view) { args.view = new labelModule.Label(); } (args.view).text = "item " + args.index; indexes[args.index] = indexes[args.index] ? indexes[args.index] + 1 : 1; completed = args.index === (listView.items.length - 1); }); TKUnit.waitUntilReady(() => { return completed; }, ASYNC); TKUnit.assertEqual(indexes[0], 1, "itemLoading called more than once"); TKUnit.assertEqual(indexes[1], 1, "itemLoading called more than once"); TKUnit.assertEqual(indexes[2], 1, "itemLoading called more than once"); completed = false; listView.refresh(); TKUnit.waitUntilReady(() => { return completed; }, ASYNC); TKUnit.assertEqual(indexes[0], 2, "itemLoading not called for index 0"); TKUnit.assertEqual(indexes[1], 2, "itemLoading not called for index 1"); TKUnit.assertEqual(indexes[2], 2, "itemLoading not called for index 2"); } public test_set_itmes_to_null_clears_native_items() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); listView.items = FEW_ITEMS; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), FEW_ITEMS.length, "Native views count."); listView.items = null; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === 0; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 0, "Native views count."); } public test_set_itmes_to_undefiend_clears_native_items() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); listView.items = FEW_ITEMS; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), FEW_ITEMS.length, "Native views count."); listView.items = undefined; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === 0; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 0, "Native views count."); } public test_set_itmes_to_different_source_loads_new_items() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); listView.items = [1, 2, 3]; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "Native views count."); listView.items = ["a", "b", "c", "d"]; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 4, "Native views count."); } public test_set_items_to_observable_array_loads_all_items() { var listView = this.testView; var indexes = {}; // // ### Using ListView with ObservableArray // ``` JavaScript var colors = new observableArray.ObservableArray(["red", "green", "blue"]); listView.items = colors; listView.on(listViewModule.ListView.itemLoadingEvent, function (args: listViewModule.ItemEventData) { if (!args.view) { //// Create label if it is not already created. args.view = new labelModule.Label(); } (args.view).text = colors.getItem(args.index); indexes[args.index] = true; }); // ``` // TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assert(indexes[0], "itemLoading not called for index 0"); TKUnit.assert(indexes[1], "itemLoading not called for index 1"); TKUnit.assert(indexes[2], "itemLoading not called for index 2"); } public test_add_to_observable_array_refreshes_the_listview() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); var colors = new observableArray.ObservableArray(["red", "green", "blue"]); listView.items = colors; TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "getNativeViewCount"); // // > When using ObservableArray the list view will be automatically updated when items are added or removed form the array. // ``` JavaScript colors.push("yellow"); //// The ListView will be updated automatically. // ``` // TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 4, "getNativeViewCount"); } public test_remove_from_observable_array_refreshes_the_listview() { var listView = this.testView; var data = new observableArray.ObservableArray([1, 2, 3]); listView.items = data; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "getNativeViewCount"); data.pop(); TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 2, "getNativeViewCount"); } public test_splice_observable_array_refreshes_the_listview() { var listView = this.testView; var data = new observableArray.ObservableArray(["a", "b", "c"]); listView.items = data; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 3, "getNativeViewCount"); // Remove the first 2 elements and add data.splice(0, 2, "d", "e", "f"); TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(this.getNativeViewCount(listView), 4, "getNativeViewCount"); } public test_nativeTap_is_raised() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); listView.items = FEW_ITEMS; var nativeTapRaised = false; var itemIndex = -1; /* tslint:disable:no-unused-variable */ // // ## Responding to other events // ### ItemTap event // The event will be raise when an item inside the ListView is tapped. // ``` JavaScript listView.on(listViewModule.ListView.itemTapEvent, function (args: listViewModule.ItemEventData) { var tappedItemIndex = args.index; var tappedItemView = args.view; //// Do someting // nativeTapRaised = true; itemIndex = args.index; // }); // ``` // /* tslint:enable:no-unused-variable */ TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); this.performNativeItemTap(listView, 1); TKUnit.assert(nativeTapRaised, "itemTap not raised."); TKUnit.assertEqual(itemIndex, 1, "tappedItemIndex"); } public test_loadMoreItems_raised_when_showing_few_items() { var listView = this.testView; var loadMoreItemsCount = 0; listView.items = FEW_ITEMS; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); // // ### LoadMoreItems event // The event will be raised when the ListView is scrolled so that the last item is visible. // This even is intended to be used to add additional data in the ListView. // ``` JavaScript listView.on(listViewModule.ListView.loadMoreItemsEvent, function (data: observable.EventData) { //// Do something. // loadMoreItemsCount++; // }); // ``` // TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC); TKUnit.assertEqual(loadMoreItemsCount, 1, "loadMoreItemsCount"); } public test_loadMoreItems_not_raised_when_showing_many_items() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); var loadMoreItemsCount = 0; listView.items = MANY_ITEMS; listView.on(listViewModule.ListView.loadMoreItemsEvent, function (data: observable.EventData) { loadMoreItemsCount++; }); // We can't use waitUntilReady because we don't know what to wait for. if (platform.device.os === platform.platformNames.android) { this.waitUntilTestElementLayoutIsValid(); } else { TKUnit.wait(ASYNC); } TKUnit.assertEqual(loadMoreItemsCount, 0, "loadMoreItemsCount"); } public test_loadMoreItems_is_raised_when_scroll_to_last_item() { var listView = this.testView; listView.on(listViewModule.ListView.itemLoadingEvent, this.loadViewWithItemNumber); var loadMoreItemsCount = 0; listView.items = MANY_ITEMS; listView.on(listViewModule.ListView.loadMoreItemsEvent, function (data: observable.EventData) { loadMoreItemsCount++; }); listView.scrollToIndex(MANY_ITEMS.length - 1); // We can't use waitUntilReady because we don't know what to wait for. if (platform.device.os === platform.platformNames.android) { this.waitUntilTestElementLayoutIsValid(); } else { TKUnit.wait(ASYNC); } TKUnit.assert(loadMoreItemsCount > 0, "loadMoreItemsCount"); } public test_usingAppLevelConvertersInListViewItems() { var listView = this.testView; var dateConverter = function (value, format) { var result = format; var day = value.getDate(); result = result.replace("DD", day < 10 ? "0" + day : day); var month = value.getMonth() + 1; result = result.replace("MM", month < 10 ? "0" + month : month); result = result.replace("YYYY", value.getFullYear()); return result; }; app.resources["dateConverter"] = dateConverter; var data = new observableArray.ObservableArray(); data.push({ date: new Date(2020, 2, 7) }); listView.itemTemplate = "