diff --git a/apps/automated/src/ui/proxy-view-container/proxy-view-container-tests.ts b/apps/automated/src/ui/proxy-view-container/proxy-view-container-tests.ts index d82af831ec..2fc01674a7 100644 --- a/apps/automated/src/ui/proxy-view-container/proxy-view-container-tests.ts +++ b/apps/automated/src/ui/proxy-view-container/proxy-view-container-tests.ts @@ -273,6 +273,141 @@ export function test_proxy_inside_listview_itemTemplate_crash() { helper.buildUIAndRunTest(list, testAction); } +export function test_proxy_hidden_hides_existing_children() { + const outer = new StackLayout(); + const proxy = new ProxyViewContainer(); + const btn1 = createBtn('1'); + const btn2 = createBtn('2'); + const btn3 = createBtn('3'); + + function testAction(views: Array) { + outer.addChild(btn1); + outer.addChild(proxy); + proxy.addChild(btn2); + proxy.addChild(btn3); + outer.addChild(createBtn('4')); + + // Initially all children should be visible + assertViewHidden(btn1, false, 'btn1 should be visible initially'); + assertViewHidden(btn2, false, 'btn2 should be visible initially'); + assertViewHidden(btn3, false, 'btn3 should be visible initially'); + + // Hide the proxy container + proxy.hidden = true; + + // Proxy's isCollapsed should be set + TKUnit.assertTrue(proxy.isCollapsed, 'Proxy isCollapsed should be true when hidden'); + + // All proxy children should be hidden + assertViewHidden(btn2, true, 'btn2 should be hidden when proxy is hidden'); + assertViewHidden(btn3, true, 'btn3 should be hidden when proxy is hidden'); + + // Other children should remain visible + assertViewHidden(btn1, false, 'btn1 should remain visible'); + } + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_proxy_hidden_shows_children_when_set_to_false() { + const outer = new StackLayout(); + const proxy = new ProxyViewContainer(); + const btn1 = createBtn('1'); + const btn2 = createBtn('2'); + + function testAction(views: Array) { + outer.addChild(proxy); + proxy.addChild(btn1); + proxy.addChild(btn2); + + // Hide the proxy + proxy.hidden = true; + assertViewHidden(btn1, true, 'btn1 should be hidden'); + assertViewHidden(btn2, true, 'btn2 should be hidden'); + + // Show the proxy again + proxy.hidden = false; + + // Proxy's isCollapsed should be false + TKUnit.assertFalse(proxy.isCollapsed, 'Proxy isCollapsed should be false when not hidden'); + + // All proxy children should be visible again + assertViewHidden(btn1, false, 'btn1 should be visible when proxy is shown'); + assertViewHidden(btn2, false, 'btn2 should be visible when proxy is shown'); + } + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_proxy_hidden_hides_new_children_added_after_setting_hidden() { + const outer = new StackLayout(); + const proxy = new ProxyViewContainer(); + const btn1 = createBtn('1'); + + function testAction(views: Array) { + outer.addChild(proxy); + + // Hide the proxy first + proxy.hidden = true; + + // Add a child after hiding + proxy.addChild(btn1); + + // The new child should be hidden + assertViewHidden(btn1, true, 'New child should be hidden when added to hidden proxy'); + + // Add another child + const btn2 = createBtn('2'); + proxy.addChild(btn2); + assertViewHidden(btn2, true, 'Another new child should be hidden when added to hidden proxy'); + } + + helper.buildUIAndRunTest(outer, testAction); +} + +export function test_proxy_hidden_with_multiple_children() { + const outer = new StackLayout(); + const proxy = new ProxyViewContainer(); + const btn1 = createBtn('1'); + const btn2 = createBtn('2'); + const btn3 = createBtn('3'); + const btn4 = createBtn('4'); + + function testAction(views: Array) { + outer.addChild(proxy); + proxy.addChild(btn1); + proxy.addChild(btn2); + proxy.addChild(btn3); + proxy.addChild(btn4); + + // All should be visible initially + assertViewHidden(btn1, false, 'btn1 should be visible'); + assertViewHidden(btn2, false, 'btn2 should be visible'); + assertViewHidden(btn3, false, 'btn3 should be visible'); + assertViewHidden(btn4, false, 'btn4 should be visible'); + + // Hide the proxy + proxy.hidden = true; + + // All should be hidden + assertViewHidden(btn1, true, 'btn1 should be hidden'); + assertViewHidden(btn2, true, 'btn2 should be hidden'); + assertViewHidden(btn3, true, 'btn3 should be hidden'); + assertViewHidden(btn4, true, 'btn4 should be hidden'); + + // Show again + proxy.hidden = false; + + // All should be visible again + assertViewHidden(btn1, false, 'btn1 should be visible again'); + assertViewHidden(btn2, false, 'btn2 should be visible again'); + assertViewHidden(btn3, false, 'btn3 should be visible again'); + assertViewHidden(btn4, false, 'btn4 should be visible again'); + } + + helper.buildUIAndRunTest(outer, testAction); +} + // TODO: Proxy as a direct child to of TabItem is not supported. Not sure if we want to support it. //export function test_proxy_inside_tab() { // const proxy = new ProxyViewContainer(); @@ -336,3 +471,16 @@ function assertNativeChildren(layout: LayoutBase, arr: Array) { TKUnit.assert(false, 'No native view to assert'); } } + +function assertViewHidden(view: View, expectedHidden: boolean, message: string) { + if (view.android) { + const visibility = view.android.getVisibility(); + const isHidden = visibility === android.view.View.GONE; + TKUnit.assertEqual(isHidden, expectedHidden, message); + } else if (view.ios) { + const isHidden = view.ios.hidden; + TKUnit.assertEqual(isHidden, expectedHidden, message); + } else { + TKUnit.assert(false, 'No native view to assert hidden state'); + } +} diff --git a/packages/core/ui/proxy-view-container/index.ts b/packages/core/ui/proxy-view-container/index.ts index aafcf82f97..6623ed6676 100644 --- a/packages/core/ui/proxy-view-container/index.ts +++ b/packages/core/ui/proxy-view-container/index.ts @@ -2,6 +2,7 @@ import { View, CSSType } from '../core/view'; import { LayoutBase } from '../layouts/layout-base'; import { Property } from '../core/properties'; import { Trace } from '../../trace'; +import { hiddenProperty, booleanConverter } from '../core/view-base'; /** * Proxy view container that adds all its native children directly to the parent. @@ -103,6 +104,11 @@ export class ProxyViewContainer extends LayoutBase { } super._addViewToNativeVisualTree(child); + // Apply hidden state to new child if the container is hidden + if (this.hidden) { + child.hidden = true; + } + layoutProperties.forEach((propName) => { const proxyPropName = makeProxyPropName(propName); child[proxyPropName] = child[propName]; @@ -231,6 +237,18 @@ export class ProxyViewContainer extends LayoutBase { child[propName] = value; child[proxyPropName] = value; } + + /** + * Apply the hidden state to all child views. + * When ProxyViewContainer is hidden, all its children should also be hidden + * since they are directly added to the parent's native view tree. + */ + protected _applyHiddenToChildren(hidden: boolean): void { + this.eachChildView((child) => { + child.hidden = hidden; + return true; + }); + } } // Layout propeties to be proxyed to the child views @@ -277,6 +295,25 @@ for (const name of layoutProperties) { proxyProperty.register(ProxyViewContainer); } +// Override the hidden property to propagate it to all children +// Since ProxyViewContainer has no native view, we need to apply the hidden state +// to all children so they are visually hidden when the container is hidden. +const proxyHiddenProperty = new Property({ + name: 'hidden', + defaultValue: false, + affectsLayout: __APPLE__, + valueConverter: booleanConverter, + valueChanged(target, oldValue, newValue) { + // Call the base implementation to set isCollapsed for layout calculations + if (target) { + target.isCollapsed = !!newValue; + } + // Apply hidden state to all children + target._applyHiddenToChildren(!!newValue); + }, +}); +proxyHiddenProperty.register(ProxyViewContainer); + function makeProxyPropName(propName) { return `_proxy:${propName}`; }