Skip to content

feat: present Vertical Bars popovers from native SwiftUI buttons - #273

Merged
rdlabo merged 5 commits into
mainfrom
feat/vertical-bars-native-popover
Oct 15, 2026
Merged

rdlabo merged 5 commits into
mainfrom
feat/vertical-bars-native-popover

Conversation

@rdlabo

@rdlabo rdlabo commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Opening an Ionic popover from a native Vertical Bars button currently retires the rail to Web rendering. This change keeps the existing native controls projected while presenting the live popover content from that button through SwiftUI.

This is a focused extraction of #260, based on current main. The child WebView prepares concurrently with Ionic content initialization and the Web enter animation is bypassed. Only accepted Vertical Bars ion-button anchors are eligible; ordinary toolbar/body popovers, modals, sheets and alerts keep their existing behavior. controls.popover is opt-in and defaults to false.

Ionic continues to own lifecycle, application handlers and dismissal. Missing native anchors or preparation failures restore the Web popover. A nested overlay, menu, page departure, resize or shell suspension releases the retained projection. The source WebView is neither resized nor reparented by this feature.

Backdrop dismissal keeps the live host until SwiftUI reports disappearance, and binding notifications are scoped to their owning anchor. A tap accepted by the same native anchor while Ionic is still dismissing is coalesced and replayed after cleanup, with the normal source/revision/disabled checks.

Cancellation before SwiftUI appears now settles the pending presentation and releases its model, so the next popover can open. Native dismissal rejection or timeout still completes Ionic’s dismissal lifecycle and preserves dismissal data/role.

The child document is initialized in standards mode and waits for copied stylesheets before presentation, preventing stretched Ionic buttons in WebKit.

Validation:

  • Library build, demo production build, and formatting checks passed.
  • Demo unit tests: 50 passed across 11 files.
  • Focused Playwright coverage: 10 passed (full shell and standalone retention, ordinary Web popovers, preparation failure, dismissal-time activation replay/coalescing, disabled-source cancellation, dismissal reject/timeout and nested source/inline overlays); existing modal/FAB regression checks also passed.
  • The dismissal-time activation regression test failed before the fix and passed after it; the input was also observed reaching the native anchor during Ionic dismissal in simulator logs.
  • Xcode 27.1 simulator build passed. Swift regression for cancellation before the first SwiftUI appearance and repeated reopening passed.
  • Dismissal reject/timeout tests failed before the lifecycle fix and passed after it.
  • iPhone Duo / iOS 27.1 XCTest passed: 20 alternating button/backdrop open/dismiss cycles, live Angular counter, close button and backdrop dismissal, intrinsic button height, and unchanged source WebView/background/anchor positions. Before/open/after screenshots inspected.

The existing preview limitations for arbitrary cross-document CSS and complete accessibility parity still apply. No package version change.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment on lines +199 to +203
if (current) {
const connection = current;
current = undefined;
opening = opening.then(() => release(connection)).catch(console.error);
if (!connection.dismissed) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 ポップオーバー内で開いた選択画面が消える

リレー中のポップオーバーで ion-select などを開くと、新しいオーバーレイの開始時に親の接続が解除されます。親の子 WebView が閉じられるため、選択画面も消えて操作できません。

Learn more

子 WebView にもオーバーレイの開始イベントを登録するため、内部の ion-select などが開くポップオーバーも present() に届きます。current が存在する場合、この分岐は新しいオーバーレイの所在を調べずに親を解放します。cleanup はその子 WebView を閉じるので、新しいオーバーレイも失われます。

Example: リレーしたポップオーバー内の ion-select を押すと、選択肢のポップオーバーが子 WebView で開き始めます。その開始イベントで親のリレーが終了し、選択肢を表示している WebView ごと閉じられます。

Recommended fix: 子ドキュメントで開始したオーバーレイについては、閉じる前にそのコンテンツとライフサイクルを元のドキュメントへ移すか、子オーバーレイが終了するまで WebView を維持してください。子と元のドキュメント双方での入れ子のオーバーレイを検証してください。

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +58 to +62
for (const node of ancestors) {
const shell = destination.createElement(node.localName);
mirror(node, shell, true);
parent.append(shell);
parent = shell;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 カスタムポップオーバーのスタイル適用範囲

祖先要素を複製して display: contents に変えるため、元ページのレイアウト条件に依存するスタイルは再現されない可能性があります。対応するスタイル構成を確認してください。

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Playwright test results

passed  399 passed
flaky  1 flaky

Details

stats  400 tests across 21 suites
duration  4 minutes, 40 seconds
commit  6ba2639
info  This detailed result covers Ionic 9 only. Ionic 8 runs against the same screenshots in a separate matrix job; check the workflow run for both results. To update the screenshots, comment with /update-screenshots.

Flaky tests

chromium › range-interaction.spec.ts › activeBarStart never shows a reflection on the wrong side in ltr

github-actions Bot added a commit that referenced this pull request Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

📊 Ionic 9 Playwright Test Report

View the detailed Ionic 9 report: https://rdlabo-dev.github.io/ionic-theme-ios27/pr-273/

Ionic 8 runs against the same screenshots in a separate matrix job. View both results in the workflow run.

github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
@rdlabo
rdlabo merged commit e866fb1 into main Oct 15, 2026
19 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

npm beta published

CI passed for the merge commit e866fb1dca9f. Install the immutable version with:

npm install @rdlabo/ionic-theme-ios27@1.2.1-beta.pr273.shae866fb1dca9f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant