Skip to content

Add example app and harden auto-height bridge#5

Merged
mCodex merged 3 commits intomainfrom
refactor/core
May 5, 2026
Merged

Add example app and harden auto-height bridge#5
mCodex merged 3 commits intomainfrom
refactor/core

Conversation

@mCodex
Copy link
Copy Markdown
Owner

@mCodex mCodex commented May 5, 2026

This pull request refactors the example app for react-native-sized-webview to improve clarity, modularity, and maintainability. The showcase app is restructured into modular demo sections, with new reusable components and a shared theme for consistent styling. The documentation is also updated to more clearly explain the auto-sizing pipeline and demo scenarios.

App modularization and demo improvements:

  • The main example app (App.tsx) is refactored to use four modular demo sections: IntroDemo, RemoteSitePicker, GoogleFontDemo, and LongArticleDemo, each demonstrating a key scenario for auto-sizing WebViews. This replaces the previous monolithic implementation and moves static HTML samples to a new articleSamples.ts data module. [1] [2]

Reusable UI components and shared theme:

  • Adds reusable components PillButton (for toggle buttons) and SectionHeader (for section headers), styled with a new shared theme (colors, spacing, radius). This ensures visual consistency and reduces style duplication across demo sections. [1] [2] [3]

Documentation and explanation improvements:

  • The README.md is rewritten to clearly describe the four demo scenarios, provide a detailed breakdown of the auto-sizing pipeline, and explain the performance characteristics and React Compiler integration. [1] [2] [3]

React Compiler integration:

  • Adds babel-plugin-react-compiler to the example app’s Babel config and dependencies, ensuring compatibility with React Compiler and demonstrating its use in a real-world app. [1] [2]

References:

Add a full example app (modular demo sections, shared theme, UI components, and HTML/data samples) to showcase four sizing scenarios: local HTML, remote sites, Google Font loading, and long-form articles. Update example tooling to run babel-plugin-react-compiler and add it to devDependencies.

Refine the library internals and docs: expand README with measurement details and performance notes; improve SizedWebView docs and injection strategy (inject bridge at both documentStart and after-load, composeInjectedScript now includes the bridge only when JS is enabled).

Rewrite and harden AUTO_HEIGHT_BRIDGE: switch to a multi-source measurement (scroll/offset + last non-inert child bounding rect + computed margin), replace bounded fallback counter with a bootstrap-grace window, expose a tiny frozen global handle (refresh/destroy/version) to avoid tampering, remove earlier wrapper mutation approach, and improve cleanup/idempotency.

Tests updated: include new payload-rejection test for prefixed non-decimal messages and adapt existing tests to expect AUTO_HEIGHT_BRIDGE to be composed into injected script.

Overall: adds example UX, visual tokens, and demos while making the auto-height measurement more robust and secure.
@mCodex mCodex self-assigned this May 5, 2026
Copilot AI review requested due to automatic review settings May 5, 2026 13:03
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates react-native-sized-webview’s auto-height pipeline (bridge + message parsing) and restructures the Expo example app into modular demo sections, alongside documentation updates describing the sizing approach.

Changes:

  • Hardened the auto-height message protocol parsing and expanded hook/component docs/tests around the bridge payload shape.
  • Refactored the injected bridge script to a multi-source, non-mutating height measurement approach with an adaptive “bootstrap grace” fallback.
  • Rebuilt the example app into themed, reusable UI components and four scenario-focused demo sections; updated README to explain scenarios and internals.

Reviewed changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/hooks/useAutoHeight.ts Tightens/clarifies parsing of bridge payloads and expands hook documentation.
src/constants/autoHeightBridge.ts Refactors the injected bridge: measurement logic, fallback strategy, hardened public handle.
src/components/SizedWebView.tsx Updates docs and injects the bridge in both pre-load and post-load hooks for reliability.
src/__tests__/useAutoHeight.test.tsx Adds tests ensuring forged/non-decimal prefixed payloads are rejected.
src/__tests__/index.test.tsx Updates expectations for the new injection composition (bridge included post-load).
README.md Rewrites/expands usage, scenarios, and pipeline explanation (currently has some drift vs implementation).
example/src/styles/theme.ts Introduces shared theme tokens (colors/spacing/radius) for the example app.
example/src/sections/RemoteSitePicker.tsx Adds a modular demo for swapping among real remote sites.
example/src/sections/LongArticleDemo.tsx Adds a modular long-article demo to exercise late-load + trailing margin cases.
example/src/sections/IntroDemo.tsx Adds a modular local-HTML mutation demo driven by a toggle.
example/src/sections/GoogleFontDemo.tsx Adds a modular demo for late font reflow (Google Fonts).
example/src/data/remotePages.ts Extracts the remote-site catalog into a data module.
example/src/data/googleFontDemo.ts Adds the local HTML payload used for the font-loading scenario.
example/src/data/articleSamples.ts Extracts static HTML samples and helper composition functions.
example/src/components/SectionHeader.tsx Adds a reusable section header component.
example/src/components/PillButton.tsx Adds a reusable pill toggle button component.
example/src/App.tsx Refactors the app shell to render the four modular demo sections + themed styling.
example/package.json Adds React Compiler Babel plugin dependency for the example app.
example/babel.config.js Ensures the React Compiler plugin runs before other Babel transforms.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/hooks/useAutoHeight.ts Outdated
Comment on lines 74 to 90
const BRIDGE_NUMBER_PATTERN = /^\d+(?:\.\d+)?$/;

const parseHeightPayload = (rawValue: unknown): number | null => {
let candidate: unknown;
let numericValue: number;

if (typeof rawValue === 'number') {
candidate = rawValue;
numericValue = rawValue;
} else if (
typeof rawValue === 'string' &&
rawValue.startsWith(BRIDGE_MESSAGE_PREFIX)
) {
candidate = rawValue.slice(BRIDGE_MESSAGE_PREFIX.length);
const suffix = rawValue.slice(BRIDGE_MESSAGE_PREFIX.length);
if (!BRIDGE_NUMBER_PATTERN.test(suffix)) {
return null;
}
numericValue = Number(suffix);
} else {
Comment on lines +123 to +136
* ```tsx
* import { WebView } from 'react-native-webview';
* import { AUTO_HEIGHT_BRIDGE, useAutoHeight } from 'react-native-sized-webview';
*
* function CustomSizedView({ html }: { html: string }) {
* const { height, setHeightFromPayload } = useAutoHeight({ minHeight: 0 });
* return (
* <View style={{ height }}>
* <WebView
* source={{ html }}
* injectedJavaScriptBeforeContentLoaded={AUTO_HEIGHT_BRIDGE}
* onMessage={(e) => setHeightFromPayload(e.nativeEvent.data)}
* />
* </View>
Comment on lines +10 to +15
* @remarks
* ## Measurement algorithm (O(1))
*
* Every measurement is the `Math.max` of multiple authoritative layout
* sources, **without mutating** the host page's DOM or styles:
*
Comment thread src/constants/autoHeightBridge.ts Outdated
* child's bottom margin escapes `<body>`) and late-reflow scenarios
* where `scrollHeight` momentarily under-reports on iOS WKWebView.
* `getBoundingClientRect` is part of the CSSOM View spec and returns
* document-layout coordinates, NOT viewport-clamped values.
Comment thread src/constants/autoHeightBridge.ts Outdated
Comment on lines 271 to 284
@@ -194,106 +283,35 @@ export const AUTO_HEIGHT_BRIDGE: string = `(() => {
AUDIO: true,
};
Comment thread README.md Outdated
Comment on lines +154 to +160
- **Re-parents body children into a dedicated wrapper.** The wrapper has no explicit height and no `overflow: hidden`, so its layout is never clamped by the native frame size.
- **Multi-source measurement (the production-grade fix).** Each measurement is the `Math.max` of four authoritative layout sources:
1. `wrapper.scrollHeight` / `wrapper.offsetHeight` — primary, fastest, accurate for normal block flow.
2. `document.body.scrollHeight` / `documentElement.scrollHeight` — backstop when frameworks style `body`/`html` directly.
3. The last non-inert child's `getBoundingClientRect().bottom + computedMarginBottom` — catches margin-collapse, late image reflow, and trailing absolutely-positioned content where `scrollHeight` momentarily under-reports on iOS WKWebView.

Inert siblings (`SCRIPT`, `STYLE`, `META`, `LINK`, `TITLE`, `HEAD`, `NOSCRIPT`) are skipped during the last-child walk so they never short-circuit the probe.
Comment thread README.md Outdated
Comment on lines +152 to +156
The injected bridge runs once per page, before content loads, and turns the WebView into a self-measuring component:

- **Re-parents body children into a dedicated wrapper.** The wrapper has no explicit height and no `overflow: hidden`, so its layout is never clamped by the native frame size.
- **Multi-source measurement (the production-grade fix).** Each measurement is the `Math.max` of four authoritative layout sources:
1. `wrapper.scrollHeight` / `wrapper.offsetHeight` — primary, fastest, accurate for normal block flow.
Clarify bridge behavior and measurement algorithm, and tighten payload parsing to ASCII-digit integers.

Changes:
- README: Explain the bridge is idempotent, measures the page in-place (no re-parenting or injected inline styles), update measurement sources order, and note skipping of inert and out-of-flow elements. Clarify iOS WKWebView/getBoundingClientRect behavior and bootstrap grace logic.
- src/constants/autoHeightBridge.ts: Update comments to describe O(k) complexity, clarify getBoundingClientRect semantics, and remove an unused RENDERABLE_MEDIA_TAGS block from the injected bridge string.
- src/hooks/useAutoHeight.ts: Only accept namespaced integer payloads from the bridge (changed regex from allowing decimals to /^
\d+$/) and update explanatory comments and example imports.
- src/__tests__/useAutoHeight.test.tsx: Update test name/text to expect integer-only payloads and add decimal payloads to forged inputs.

Rationale: Improve documentation accuracy about how the injected bridge operates, tighten input validation to reject fractional or otherwise coerced numeric payloads, and keep the injected script lean by removing unused tag metadata.
@mCodex mCodex merged commit 80773f8 into main May 5, 2026
4 checks passed
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.

2 participants