From ad902daee5c9db0134f91d41430069e3ef1da5a4 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Tue, 23 Jun 2026 17:11:59 +0300 Subject: [PATCH 1/9] added prerender support for Popup family --- Popup/Popup.js | 16 ++- PopupTabLayout/PopupTabLayout.js | 17 ++- .../PopupTabLayout-prerender-poc-specs.js | 100 ++++++++++++++++++ TabLayout/RefocusDecorator.js | 5 +- .../qa-popuptablayout-prerender/package.json | 36 +++++++ .../resources/ilibmanifest.json | 3 + .../src/App/App.js | 13 +++ .../src/App/App.module.less | 3 + .../src/App/package.json | 3 + .../qa-popuptablayout-prerender/src/index.js | 17 +++ .../src/views/MainPanel.js | 72 +++++++++++++ 11 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 PopupTabLayout/tests/PopupTabLayout-prerender-poc-specs.js create mode 100644 samples/qa-popuptablayout-prerender/package.json create mode 100644 samples/qa-popuptablayout-prerender/resources/ilibmanifest.json create mode 100644 samples/qa-popuptablayout-prerender/src/App/App.js create mode 100644 samples/qa-popuptablayout-prerender/src/App/App.module.less create mode 100644 samples/qa-popuptablayout-prerender/src/App/package.json create mode 100644 samples/qa-popuptablayout-prerender/src/index.js create mode 100644 samples/qa-popuptablayout-prerender/src/views/MainPanel.js diff --git a/Popup/Popup.js b/Popup/Popup.js index 80c6e6368..a67bf80bc 100644 --- a/Popup/Popup.js +++ b/Popup/Popup.js @@ -303,7 +303,7 @@ const Popup = (props) => { checkPropTypes(Popup, props); const componentProps = setDefaultProps(props, popupDefaultProps); - const {noAnimation, noAutoDismiss, no5WayClose, onClose, open, position, scrimType, spotlightRestrict, ...rest} = componentProps; + const {noAnimation, noAutoDismiss, no5WayClose, onClose, open, position, prerender, scrimType, spotlightRestrict, ...rest} = componentProps; // Assign the needed props to the rest object for the child component Object.assign(rest, {noAnimation, position, spotlightRestrict}); @@ -544,6 +544,7 @@ const Popup = (props) => { open={floatLayerOpen} onOpen={handleFloatingLayerOpen} onDismiss={handleDismiss} + prerender={prerender} scrimType={scrimType} > ( + + + + + Brightness + Contrast + + + + + + + Balance + + + + + + + Auto Tuning + + + + + + + Network + + + + +); + +const prerender = (element) => renderToString( + {element} +); + +describe('PopupTabLayout prerendering PoC', () => { + const tabTitles = ['Picture', 'Sound', 'Channels', 'Connection']; + + test('default (portal) path: a single renderToString pass produces NO popup content', () => { + // The default path renders through Popup -> FloatingLayer, whose first render returns + // null until `readyToRender` flips post-mount. A prerender pass never gets that second + // render, so the tab content is absent from the HTML + const html = prerender(); + + tabTitles.forEach((title) => { + expect(html).not.toContain(title); + }); + }); + + test('optimized path: a single renderToString pass produces the full 4-tab content', () => { + const html = prerender(); + + // All four tab titles are present in the prerendered HTML + tabTitles.forEach((title) => { + expect(html).toContain(title); + }); + + // the content of the first (active) tab's panel is also present. + expect(html).toContain('Brightness'); + expect(html).toContain('Contrast'); + }); + + test('prerender path (real Popup/FloatingLayer): a single renderToString pass produces the full 4-tab content', () => { + // Unlike `optimized`, this keeps the real Popup + FloatingLayer; the `prerender` prop makes + // FloatingLayer render its content inline during the prerender pass instead of returning null. + const html = prerender(); + + tabTitles.forEach((title) => { + expect(html).toContain(title); + }); + + expect(html).toContain('Brightness'); + expect(html).toContain('Contrast'); + }); +}); diff --git a/TabLayout/RefocusDecorator.js b/TabLayout/RefocusDecorator.js index 85d0de8ad..16cb0b8e2 100644 --- a/TabLayout/RefocusDecorator.js +++ b/TabLayout/RefocusDecorator.js @@ -1,3 +1,4 @@ +import {isWindowReady} from '@enact/core/snapshot'; import {checkPropTypes} from '@enact/core/util'; import Spotlight from '@enact/spotlight'; import {useId} from '@enact/ui/internal/IdProvider'; @@ -22,8 +23,10 @@ const getNavigableFilter = (spotlightId, collapsed) => (elem) => ( ); function useScreenOrientation () { + // Guard against a prerender/SSR pass where `window` is unavailable. The orientation defaults to + // 'landscape' until a window is ready; the resize effect keeps it current on the client. const getOrientation = () => - window.innerWidth > window.innerHeight ? 'landscape' : 'portrait'; + (isWindowReady() && window.innerWidth <= window.innerHeight) ? 'portrait' : 'landscape'; const [orientation, setOrientation] = useState(getOrientation()); diff --git a/samples/qa-popuptablayout-prerender/package.json b/samples/qa-popuptablayout-prerender/package.json new file mode 100644 index 000000000..8a73e1365 --- /dev/null +++ b/samples/qa-popuptablayout-prerender/package.json @@ -0,0 +1,36 @@ +{ + "name": "qa-popuptablayout-prerender", + "version": "0.1.0", + "description": "A qa sample for verifying prerendering of PopupTabLayout", + "author": "", + "main": "src/index.js", + "scripts": { + "serve": "enact serve", + "pack": "enact pack", + "pack-p": "enact pack -p", + "watch": "enact pack --watch", + "clean": "enact clean", + "lint": "enact lint --strict .", + "test": "enact test", + "test-watch": "enact test --watch" + }, + "license": "Apache-2.0", + "private": true, + "repository": "", + "enact": { + "title": "QA PopupTabLayout Prerender", + "theme": "limestone", + "isomorphic": true + }, + "dependencies": { + "@enact/core": "^5.5.1", + "@enact/i18n": "^5.5.1", + "@enact/limestone": "../../", + "@enact/spotlight": "^5.5.1", + "@enact/ui": "^5.5.1", + "ilib": "^14.22.0", + "prop-types": "^15.8.1", + "react": "^19.2.7", + "react-dom": "^19.2.7" + } +} diff --git a/samples/qa-popuptablayout-prerender/resources/ilibmanifest.json b/samples/qa-popuptablayout-prerender/resources/ilibmanifest.json new file mode 100644 index 000000000..d946318dc --- /dev/null +++ b/samples/qa-popuptablayout-prerender/resources/ilibmanifest.json @@ -0,0 +1,3 @@ +{ + "files": [] +} diff --git a/samples/qa-popuptablayout-prerender/src/App/App.js b/samples/qa-popuptablayout-prerender/src/App/App.js new file mode 100644 index 000000000..18caf0b24 --- /dev/null +++ b/samples/qa-popuptablayout-prerender/src/App/App.js @@ -0,0 +1,13 @@ +import ThemeDecorator from '@enact/limestone/ThemeDecorator'; + +import MainPanel from '../views/MainPanel'; + +import css from './App.module.less'; + +const App = (props) => ( +
+ +
+); + +export default ThemeDecorator(App); diff --git a/samples/qa-popuptablayout-prerender/src/App/App.module.less b/samples/qa-popuptablayout-prerender/src/App/App.module.less new file mode 100644 index 000000000..f9db12c85 --- /dev/null +++ b/samples/qa-popuptablayout-prerender/src/App/App.module.less @@ -0,0 +1,3 @@ +.app { + /* styles can be put here */ +} diff --git a/samples/qa-popuptablayout-prerender/src/App/package.json b/samples/qa-popuptablayout-prerender/src/App/package.json new file mode 100644 index 000000000..bf7e48160 --- /dev/null +++ b/samples/qa-popuptablayout-prerender/src/App/package.json @@ -0,0 +1,3 @@ +{ + "main": "App.js" +} diff --git a/samples/qa-popuptablayout-prerender/src/index.js b/samples/qa-popuptablayout-prerender/src/index.js new file mode 100644 index 000000000..a601c327f --- /dev/null +++ b/samples/qa-popuptablayout-prerender/src/index.js @@ -0,0 +1,17 @@ +/* global ENACT_PACK_ISOMORPHIC */ +import {createRoot, hydrateRoot} from 'react-dom/client'; + +import App from './App'; + +const appElement = (); + +// In a browser environment, render instead of exporting +if (typeof window !== 'undefined') { + if (ENACT_PACK_ISOMORPHIC) { + hydrateRoot(document.getElementById('root'), appElement); + } else { + createRoot(document.getElementById('root')).render(appElement); + } +} + +export default appElement; diff --git a/samples/qa-popuptablayout-prerender/src/views/MainPanel.js b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js new file mode 100644 index 000000000..0ed115a23 --- /dev/null +++ b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js @@ -0,0 +1,72 @@ +import BodyText from '@enact/limestone/BodyText'; +import Button from '@enact/limestone/Button'; +import Heading from '@enact/limestone/Heading'; +import Item from '@enact/limestone/Item'; +import {Header} from '@enact/limestone/Panels'; +import PopupTabLayout, {Tab, TabPanel, TabPanels} from '@enact/limestone/PopupTabLayout'; +import {useCallback, useState} from 'react'; + +// A 4-tab PopupTabLayout that opens on launch. With `prerender`, FloatingLayer renders the +// content inline during the prerender pass (and the initial client render), then relocates it +// into the floating layer via a portal once mounted — so the tabs appear in the prerendered HTML +// and hydrate without a mismatch. Toggle `optimized`/`prerender` below to compare. +const MainPanel = () => { + // Opened on launch so the popup is part of the initial (prerendered) render. + const [open, setOpen] = useState(true); + + const handleClose = useCallback(() => setOpen(false), []); + const handleOpen = useCallback(() => setOpen(true), []); + + return ( +
+ PopupTabLayout prerender sample + + The PopupTabLayout below opens on launch and uses the `prerender` prop, so its tab + content is captured in the prerendered HTML (view source / disable JS to verify). + + + + + + + +
+ Brightness + Contrast + Color + + + + + + +
+ Balance + Mode + + + + + + +
+ Auto Tuning + Channel Manager + + + + + + +
+ Network + Device Connector + + + + +
+ ); +}; + +export default MainPanel; From 878eb5da8e0a82f7477c47059a6560dadb0a12a5 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Wed, 24 Jun 2026 15:07:54 +0300 Subject: [PATCH 2/9] added prerender support for Popup family --- PopupTabLayout/PopupTabLayout.js | 4 ++-- samples/qa-popuptablayout-prerender/src/App/App.js | 4 +--- samples/qa-popuptablayout-prerender/src/App/App.module.less | 3 --- samples/qa-popuptablayout-prerender/src/views/MainPanel.js | 6 +----- samples/sampler/stories/default/PopupTabLayout.js | 2 ++ 5 files changed, 6 insertions(+), 13 deletions(-) delete mode 100644 samples/qa-popuptablayout-prerender/src/App/App.module.less diff --git a/PopupTabLayout/PopupTabLayout.js b/PopupTabLayout/PopupTabLayout.js index 9a0ae0aca..d915959f0 100644 --- a/PopupTabLayout/PopupTabLayout.js +++ b/PopupTabLayout/PopupTabLayout.js @@ -198,7 +198,7 @@ const PopupTabLayoutBase = kind({ * Optimizes PopupTabLayout without Popup when true. * * @type {Boolean} - * @private + * @public */ optimized: PropTypes.bool, @@ -355,7 +355,7 @@ const OptimizedFocusDecorator = hoc((config, Wrapped) => { * Optimizes PopupTabLayout without Popup when true. * * @type {Boolean} - * @private + * @public */ optimized: PropTypes.bool, diff --git a/samples/qa-popuptablayout-prerender/src/App/App.js b/samples/qa-popuptablayout-prerender/src/App/App.js index 18caf0b24..fd888042f 100644 --- a/samples/qa-popuptablayout-prerender/src/App/App.js +++ b/samples/qa-popuptablayout-prerender/src/App/App.js @@ -2,10 +2,8 @@ import ThemeDecorator from '@enact/limestone/ThemeDecorator'; import MainPanel from '../views/MainPanel'; -import css from './App.module.less'; - const App = (props) => ( -
+
); diff --git a/samples/qa-popuptablayout-prerender/src/App/App.module.less b/samples/qa-popuptablayout-prerender/src/App/App.module.less deleted file mode 100644 index f9db12c85..000000000 --- a/samples/qa-popuptablayout-prerender/src/App/App.module.less +++ /dev/null @@ -1,3 +0,0 @@ -.app { - /* styles can be put here */ -} diff --git a/samples/qa-popuptablayout-prerender/src/views/MainPanel.js b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js index 0ed115a23..e15790cad 100644 --- a/samples/qa-popuptablayout-prerender/src/views/MainPanel.js +++ b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js @@ -1,4 +1,4 @@ -import BodyText from '@enact/limestone/BodyText'; + import Button from '@enact/limestone/Button'; import Heading from '@enact/limestone/Heading'; import Item from '@enact/limestone/Item'; @@ -20,10 +20,6 @@ const MainPanel = () => { return (
PopupTabLayout prerender sample - - The PopupTabLayout below opens on launch and uses the `prerender` prop, so its tab - content is captured in the prerendered HTML (view source / disable JS to verify). - diff --git a/samples/sampler/stories/default/PopupTabLayout.js b/samples/sampler/stories/default/PopupTabLayout.js index 7557c6195..ee9108667 100644 --- a/samples/sampler/stories/default/PopupTabLayout.js +++ b/samples/sampler/stories/default/PopupTabLayout.js @@ -93,6 +93,7 @@ const PopupTabLayoutSamplesBase = (props) => { onShow={action('onShow')} scrimType={args['scrimType']} spotlightRestrict={args['spotlightRestrict']} + optimized={args['optimized']} > ; boolean('include icons', _PopupTabLayout, Config, true); +boolean('optimized', _PopupTabLayout, Config); boolean('noAnimation', _PopupTabLayout, Config); boolean('noAutoDismiss', _PopupTabLayout, Config); select( From a8406c6e379681b950c46b587cedcf654d3f96a7 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Wed, 24 Jun 2026 15:14:03 +0300 Subject: [PATCH 3/9] added prerender support for Popup family --- samples/sampler/stories/default/PopupTabLayout.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/sampler/stories/default/PopupTabLayout.js b/samples/sampler/stories/default/PopupTabLayout.js index ee9108667..ebad02507 100644 --- a/samples/sampler/stories/default/PopupTabLayout.js +++ b/samples/sampler/stories/default/PopupTabLayout.js @@ -94,6 +94,7 @@ const PopupTabLayoutSamplesBase = (props) => { scrimType={args['scrimType']} spotlightRestrict={args['spotlightRestrict']} optimized={args['optimized']} + prerendered={args['prerendered']} > ; boolean('include icons', _PopupTabLayout, Config, true); boolean('optimized', _PopupTabLayout, Config); +boolean('prerendered', _PopupTabLayout, Config); boolean('noAnimation', _PopupTabLayout, Config); boolean('noAutoDismiss', _PopupTabLayout, Config); select( From 4c6303ddb8f2ce90de241f80cc164e237309e4aa Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Wed, 24 Jun 2026 15:16:22 +0300 Subject: [PATCH 4/9] added prerender support for Popup family --- samples/sampler/stories/default/PopupTabLayout.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/sampler/stories/default/PopupTabLayout.js b/samples/sampler/stories/default/PopupTabLayout.js index ebad02507..7c385e431 100644 --- a/samples/sampler/stories/default/PopupTabLayout.js +++ b/samples/sampler/stories/default/PopupTabLayout.js @@ -94,7 +94,7 @@ const PopupTabLayoutSamplesBase = (props) => { scrimType={args['scrimType']} spotlightRestrict={args['spotlightRestrict']} optimized={args['optimized']} - prerendered={args['prerendered']} + prerender={args['prerender']} > ; boolean('include icons', _PopupTabLayout, Config, true); boolean('optimized', _PopupTabLayout, Config); -boolean('prerendered', _PopupTabLayout, Config); +boolean('prerender', _PopupTabLayout, Config); boolean('noAnimation', _PopupTabLayout, Config); boolean('noAutoDismiss', _PopupTabLayout, Config); select( From 10b0c66566b79c5018f374fe6733c31bb5c1d005 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Wed, 24 Jun 2026 15:36:59 +0300 Subject: [PATCH 5/9] added prerender support for Popup family --- .github/workflows/ci-reusable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-reusable.yml b/.github/workflows/ci-reusable.yml index be48a6a00..714e53c70 100644 --- a/.github/workflows/ci-reusable.yml +++ b/.github/workflows/ci-reusable.yml @@ -43,7 +43,7 @@ jobs: - name: Clone and setup Enact framework run: | - git clone --branch=develop --depth 1 https://github.com/enactjs/enact ../enact + git clone --branch=feature/NXT-10275 --depth 1 https://github.com/enactjs/enact ../enact pushd ../enact npm install npm run lerna exec -- --ignore enact-sampler --concurrency 1 -- npm --no-package-lock install From 940e474d30977a898b0d3b2bf22bc7a32456d2e8 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Wed, 24 Jun 2026 15:44:41 +0300 Subject: [PATCH 6/9] docs fix --- PopupTabLayout/PopupTabLayout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PopupTabLayout/PopupTabLayout.js b/PopupTabLayout/PopupTabLayout.js index d915959f0..991f889dd 100644 --- a/PopupTabLayout/PopupTabLayout.js +++ b/PopupTabLayout/PopupTabLayout.js @@ -355,7 +355,7 @@ const OptimizedFocusDecorator = hoc((config, Wrapped) => { * Optimizes PopupTabLayout without Popup when true. * * @type {Boolean} - * @public + * @private */ optimized: PropTypes.bool, From 0c00a7614046fca103a328acaf30384bf388ccdc Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Wed, 24 Jun 2026 16:14:04 +0300 Subject: [PATCH 7/9] added ui tests --- .../PopupTabLayout-prerender-specs.js | 102 ++++++++++++++++++ .../PopupTabLayout/PopupTabLayoutPage.js | 8 ++ 2 files changed, 110 insertions(+) create mode 100644 tests/ui/specs/PopupTabLayout/PopupTabLayout-prerender-specs.js diff --git a/tests/ui/specs/PopupTabLayout/PopupTabLayout-prerender-specs.js b/tests/ui/specs/PopupTabLayout/PopupTabLayout-prerender-specs.js new file mode 100644 index 000000000..322f8028c --- /dev/null +++ b/tests/ui/specs/PopupTabLayout/PopupTabLayout-prerender-specs.js @@ -0,0 +1,102 @@ +const Page = require('./PopupTabLayoutPage'); + +describe('PopupTabLayout prerender/optimized', function () { + const {popupTabLayout} = Page.components; + + describe('prerender', function () { + beforeEach(async function () { + await Page.open('', '?prerender'); + }); + + it('should render the popup open with the first tab\'s view', async function () { + await Page.waitForExist('#tabLayout'); + + const expected = 'display'; + const actual = await popupTabLayout.currentView.getAttribute('id'); + + expect(actual).toBe(expected); + }); + + it('should render its content inside the floating layer (portal) at runtime', async function () { + await Page.waitForExist('#tabLayout'); + + expect(await Page.isContentInFloatingLayer()).toBe(true); + }); + + it('should not apply the `optimized` class', async function () { + await Page.waitForExist('#tabLayout'); + + const className = await popupTabLayout.self.getAttribute('class'); + + expect(className).not.toContain('optimized'); + }); + + it('should support 5-way tab navigation like the default', async function () { + const soundId = 'sound'; + + await Page.delay(1000); + await Page.spotlightDown(); + await Page.waitForExist(`#${soundId}`); + + const expected = soundId; + const actual = await popupTabLayout.currentView.getAttribute('id'); + + expect(actual).toBe(expected); + }); + + it('should close the popup on back when the focus is on the tabs', async function () { + await Page.delay(500); + await Page.waitTransitionEnd(1500, 'waiting for popup to close', async () => { + await Page.backKey(); + }); + + await Page.delay(500); + const expected = false; + const actual = await $('#tabLayout').isExisting(); + + expect(actual).toBe(expected); + }); + }); + + describe('optimized', function () { + beforeEach(async function () { + await Page.open('', '?optimized'); + }); + + it('should render the popup open with the first tab\'s view', async function () { + await Page.waitForExist('#tabLayout'); + + const expected = 'display'; + const actual = await popupTabLayout.currentView.getAttribute('id'); + + expect(actual).toBe(expected); + }); + + it('should render its content inline, not inside the floating layer', async function () { + await Page.waitForExist('#tabLayout'); + + expect(await Page.isContentInFloatingLayer()).toBe(false); + }); + + it('should apply the `optimized` class', async function () { + await Page.waitForExist('#tabLayout'); + + const className = await popupTabLayout.self.getAttribute('class'); + + expect(className).toContain('optimized'); + }); + + it('should support 5-way tab navigation', async function () { + const soundId = 'sound'; + + await Page.delay(1000); + await Page.spotlightDown(); + await Page.waitForExist(`#${soundId}`); + + const expected = soundId; + const actual = await popupTabLayout.currentView.getAttribute('id'); + + expect(actual).toBe(expected); + }); + }); +}); diff --git a/tests/ui/specs/PopupTabLayout/PopupTabLayoutPage.js b/tests/ui/specs/PopupTabLayout/PopupTabLayoutPage.js index 4271a548a..f12cc13bf 100644 --- a/tests/ui/specs/PopupTabLayout/PopupTabLayoutPage.js +++ b/tests/ui/specs/PopupTabLayout/PopupTabLayoutPage.js @@ -92,6 +92,14 @@ class PopupTabLayoutPage extends Page { return document.activeElement.getAttribute('aria-label'); }); } + + async isContentInFloatingLayer (id = 'tabLayout') { + return await browser.execute(function (componentId) { + const floatLayer = document.getElementById('floatLayer'); + const content = document.getElementById(componentId); + return Boolean(floatLayer && content && floatLayer.contains(content)); + }, id); + } } module.exports = new PopupTabLayoutPage(); From 9b6cc4cd679bf7fcfa9d85b3ab68452fc3700227 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Wed, 24 Jun 2026 17:22:19 +0300 Subject: [PATCH 8/9] added ui tests --- Popup/Popup.js | 7 +++++-- samples/qa-popuptablayout-prerender/src/views/MainPanel.js | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Popup/Popup.js b/Popup/Popup.js index a67bf80bc..aefcae571 100644 --- a/Popup/Popup.js +++ b/Popup/Popup.js @@ -511,9 +511,12 @@ const Popup = (props) => { // If the popup is open on mount, we need to pause spotlight so nothing steals focus // while the popup is rendering. pausedRef.current.pause(); - if (getContainerNode(containerIdRef.current)) { + // With `prerender`, the content is first rendered inline and then relocated into the portal, remounting the subtree. + // Focusing now would spot the wrong element because the content's spotlight config isn't established yet, + // and that selection is restored after the relocation. + // Skip the early spot and let the post-relocation `onOpen` focus run. + if (!prerender && getContainerNode(containerIdRef.current)) { spotPopupContent(); - } } diff --git a/samples/qa-popuptablayout-prerender/src/views/MainPanel.js b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js index e15790cad..059c82419 100644 --- a/samples/qa-popuptablayout-prerender/src/views/MainPanel.js +++ b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js @@ -1,4 +1,5 @@ +import spotlight from '@enact/spotlight'; import Button from '@enact/limestone/Button'; import Heading from '@enact/limestone/Heading'; import Item from '@enact/limestone/Item'; @@ -6,6 +7,9 @@ import {Header} from '@enact/limestone/Panels'; import PopupTabLayout, {Tab, TabPanel, TabPanels} from '@enact/limestone/PopupTabLayout'; import {useCallback, useState} from 'react'; +// Force pointer mode off so spotlight 5-way focus behaves like a TV (mirrors the wdio test setup). +spotlight.setPointerMode(false); + // A 4-tab PopupTabLayout that opens on launch. With `prerender`, FloatingLayer renders the // content inline during the prerender pass (and the initial client render), then relocates it // into the floating layer via a portal once mounted — so the tabs appear in the prerendered HTML From 44e2fcff1f1dcf3b3e5f7ec8a541945d65f56873 Mon Sep 17 00:00:00 2001 From: daniel-stoian-lgp Date: Thu, 25 Jun 2026 13:15:24 +0300 Subject: [PATCH 9/9] added ui tests --- samples/qa-popuptablayout-prerender/src/views/MainPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/qa-popuptablayout-prerender/src/views/MainPanel.js b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js index 059c82419..185854b4b 100644 --- a/samples/qa-popuptablayout-prerender/src/views/MainPanel.js +++ b/samples/qa-popuptablayout-prerender/src/views/MainPanel.js @@ -46,7 +46,7 @@ const MainPanel = () => { - +