From e9f06ed4cc3c34fddc8a78a77e58b99b3d016842 Mon Sep 17 00:00:00 2001 From: Fadhlan Ridhwanallah Date: Thu, 5 Feb 2026 12:18:16 +0700 Subject: [PATCH 1/2] Close stack when outside area is clicked in host mode --- .../host/app/components/host-mode/stack.gts | 152 ++++++++++++------ .../host/tests/acceptance/host-mode-test.gts | 54 +++++++ 2 files changed, 159 insertions(+), 47 deletions(-) diff --git a/packages/host/app/components/host-mode/stack.gts b/packages/host/app/components/host-mode/stack.gts index 9f54580676..c71ecaf0d7 100644 --- a/packages/host/app/components/host-mode/stack.gts +++ b/packages/host/app/components/host-mode/stack.gts @@ -1,4 +1,6 @@ -import type { TemplateOnlyComponent } from '@ember/component/template-only'; +import { on } from '@ember/modifier'; +import { action } from '@ember/object'; +import Component from '@glimmer/component'; import HostModeStackItem from './stack-item'; @@ -10,57 +12,113 @@ interface Signature { }; } -const HostModeStack: TemplateOnlyComponent = ; -export default HostModeStack; + @media print { + .backdrop-overlay { + display: none; + } + } + + +} diff --git a/packages/host/tests/acceptance/host-mode-test.gts b/packages/host/tests/acceptance/host-mode-test.gts index a234324827..b3bb53c7fa 100644 --- a/packages/host/tests/acceptance/host-mode-test.gts +++ b/packages/host/tests/acceptance/host-mode-test.gts @@ -427,6 +427,60 @@ module('Acceptance | host mode tests', function (hooks) { ); }); + test('clicking the stack backdrop closes the top card', async function (assert) { + let hostModeStackValue = encodeURIComponent( + JSON.stringify([`${testHostModeRealmURL}index`]), + ); + await visit(`/test/Pet/mango.json?hostModeStack=${hostModeStackValue}`); + + // Wait for stack item to appear + await waitFor( + `[data-test-host-mode-stack-item="${testHostModeRealmURL}index"]`, + ); + + // Verify stack item exists + assert + .dom(`[data-test-host-mode-stack-item="${testHostModeRealmURL}index"]`) + .exists(); + + // Click backdrop + await click('[data-test-host-mode-stack-backdrop]'); + + // Stack item should be removed + await waitUntil(() => { + return !document.querySelector( + `[data-test-host-mode-stack-item="${testHostModeRealmURL}index"]`, + ); + }); + assert + .dom(`[data-test-host-mode-stack-item="${testHostModeRealmURL}index"]`) + .doesNotExist(); + }); + + test('clicking on a stack card does not close it', async function (assert) { + let hostModeStackValue = encodeURIComponent( + JSON.stringify([`${testHostModeRealmURL}index`]), + ); + await visit(`/test/Pet/mango.json?hostModeStack=${hostModeStackValue}`); + + let stackSelector = `[data-test-host-mode-stack-item="${testHostModeRealmURL}index"]`; + assert.dom(stackSelector).exists(); + + // Click on the card content itself + await click(stackSelector); + + // Card should still exist + assert.dom(stackSelector).exists(); + }); + + test('backdrop click with empty stack does nothing', async function (assert) { + // Visit card with no stack + await visit('/test/Pet/mango.json'); + + // Stack backdrop shouldn't exist when there's no stack + assert.dom('[data-test-host-mode-stack-backdrop]').doesNotExist(); + }); + module('with a custom subdomain', function (hooks) { hooks.beforeEach(function (this) { let owner = getOwner(this)!; From c2522eda3997c76e3fe7c7b1cc0fa0ada38f3987 Mon Sep 17 00:00:00 2001 From: Fadhlan Ridhwanallah Date: Mon, 9 Feb 2026 09:57:19 +0700 Subject: [PATCH 2/2] Update approach --- .../host/app/components/host-mode/stack.gts | 80 ++++++------------- .../host/tests/acceptance/host-mode-test.gts | 10 +-- 2 files changed, 29 insertions(+), 61 deletions(-) diff --git a/packages/host/app/components/host-mode/stack.gts b/packages/host/app/components/host-mode/stack.gts index c71ecaf0d7..97878ae496 100644 --- a/packages/host/app/components/host-mode/stack.gts +++ b/packages/host/app/components/host-mode/stack.gts @@ -1,7 +1,8 @@ -import { on } from '@ember/modifier'; import { action } from '@ember/object'; import Component from '@glimmer/component'; +import onClickOutside from 'ember-click-outside/modifiers/on-click-outside'; + import HostModeStackItem from './stack-item'; interface Signature { @@ -14,18 +15,7 @@ interface Signature { export default class HostModeStack extends Component { @action - handleBackdropClick(event: MouseEvent) { - // Only handle clicks directly on the inner div or backdrop, not on children - const target = event.target as HTMLElement; - if ( - !target.classList.contains('inner') && - !target.classList.contains('backdrop-overlay') && - !target.closest('.backdrop-overlay') - ) { - return; - } - - // Close the top card (last in array) + closeTopCard() { if (this.args.close && this.args.stackItemCardIds.length > 0) { const topCardId = this.args.stackItemCardIds[this.args.stackItemCardIds.length - 1]; @@ -34,27 +24,25 @@ export default class HostModeStack extends Component { } diff --git a/packages/host/tests/acceptance/host-mode-test.gts b/packages/host/tests/acceptance/host-mode-test.gts index b3bb53c7fa..338b616ef0 100644 --- a/packages/host/tests/acceptance/host-mode-test.gts +++ b/packages/host/tests/acceptance/host-mode-test.gts @@ -443,8 +443,8 @@ module('Acceptance | host mode tests', function (hooks) { .dom(`[data-test-host-mode-stack-item="${testHostModeRealmURL}index"]`) .exists(); - // Click backdrop - await click('[data-test-host-mode-stack-backdrop]'); + // Click outside the stack items (on the stack backdrop area) + await click('[data-test-host-mode-stack]'); // Stack item should be removed await waitUntil(() => { @@ -473,12 +473,12 @@ module('Acceptance | host mode tests', function (hooks) { assert.dom(stackSelector).exists(); }); - test('backdrop click with empty stack does nothing', async function (assert) { + test('stack does not exist when there are no stacked cards', async function (assert) { // Visit card with no stack await visit('/test/Pet/mango.json'); - // Stack backdrop shouldn't exist when there's no stack - assert.dom('[data-test-host-mode-stack-backdrop]').doesNotExist(); + // Stack shouldn't exist when there are no stacked cards + assert.dom('[data-test-host-mode-stack]').doesNotExist(); }); module('with a custom subdomain', function (hooks) {