Product
Socket Now Supports uv.lock Files
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
@formidable-webview/webshell
Advanced tools
🚀 A Higher-order component to handle WebView DOM events in React Native
:warning: This library is in early development.
@formidable-webview/webshell
exports a simple builder-function (HOC) which
creates an augmented WebView
with additional DOM-based features. The returned
component behaves exactly like a WebView
, and given a ref
prop, you'll get
the underlying WebView
instance and be able to access its imperative methods.
The features provide an abstraction layer of the message
system.
Each DOM feature augments the WebView
with a DOM message handler prop, for example omDOMLinkPress
.
Here are examples of features you can assemble in the shell:
linkPressFeature
);elementDimensionsFeature
);And you can, of course, implement any DOM feature you'd like. This library also presents a set of good practices to test features in isolation and, by extension, test any injected scripts. See the tooling section.
It uses WebView.injectedJavascript
prop to inject javascript “features” (snippets) responsible
for communicating information to the Webshell
controller through DOM
handlers. Each DOM feature is try-catched in siloed error-flows.
npm install @formidable-webview/webshell
This library is fairly easy to use. Just create a shell by invoking makeShell
function with a WebView
component and the assembled features you wish the
shell to implement. Each DOM feature can be assembled with options, which alter
their behavior. For example, the linkPressFeature
default behavior prevents
click events on anchors to propagate, effectively disallowing redirects. You
can, however, change this behavior to let redirects happen while still being
notified by setting preventDefault
option to false
.
// integration/basic.tsx
import React, { useCallback } from 'react';
import { Linking } from 'react-native';
import makeWebshell, {
linkPressFeature,
elementDimensionsFeature,
ElementDimensionsObject
} from '@formidable-webview/webshell';
import WebView from 'react-native-webview';
const Webshell = makeWebshell(
WebView,
linkPressFeature.assemble({ preventDefault: true }),
elementDimensionsFeature.assemble({ tagName: 'body' })
);
export default function EnhancedWebView(webViewProps) {
const onLinkPress = useCallback((url: string) => Linking.openURL(url), []);
const onBodyDimensions = useCallback(
({ width, height }: ElementDimensionsObject) => console.info(width, height),
[]
);
const onError = useCallback((featureIdentifier, errorMessage) => {
if (featureIdentifier === linkPressFeature.identifier) {
// Handle linkPress error
console.error(errorMessage);
} else if (featureIdentifier === elementDimensionsFeature.identifier) {
// Handle dimensions error
console.error(errorMessage);
}
}, []);
return (
<Webshell
onDOMLinkPress={onLinkPress}
onDOMElementDimensions={onBodyDimensions}
onDOMError={onError}
{...webViewProps}
/>
);
}
This component height automatically and dynamically adapts to the page height, even after DOM is mounted.
// integration/autoheight.tsx
import React, { useCallback, useState } from 'react';
import makeWebshell, {
elementDimensionsFeature,
ElementDimensionsObject
} from '@formidable-webview/webshell';
import WebView from 'react-native-webview';
const Webshell = makeWebshell(
WebView,
elementDimensionsFeature.assemble({ tagName: 'body' })
);
export default function AutoheightWebView(webViewProps) {
const [height, setHeight] = useState<number | undefined>(undefined);
const onBodyDimensions = useCallback(
({ height: bodyHeight }: ElementDimensionsObject) => setHeight(bodyHeight),
[]
);
return (
<Webshell
onDOMElementDimensions={onBodyDimensions}
{...webViewProps}
style={[webViewProps.style, { height }]}
/>
);
}
To have a good sense on how to make new features, we will take a look at the
linkPressFeature
implementation. The implementation is in typescript, which
is convenient to communicate the different types implied in a DOM feature. There
are four important areas to specify in such feature:
linkPressScript
.LinkPressOptions
Webshell
? → onDOMLinkPress
string
:warning: Note that you will need to replace relative imports with direct imports from the library,
import {...} from "@formidable-webview/webshell";
// src/features/link-press.ts
import linkPressScript from './link-press.webjs';
import { makeFeature } from '../make-feature';
import type { Feature } from '../types';
/**
* An object describing customization for the linkPress feature.
*
* @public
*/
export interface LinkPressOptions {
/**
* Prevent click events on anchors to propagate.
*
* @defaultValue true
*/
preventDefault?: boolean;
}
/**
* This feature allows to intercept clicks on anchors (`<a>`). By default, it
* will prevent the click from propagating. But you can disable this option.
*
* @public
*/
export const linkPressFeature: Feature<
LinkPressOptions,
'onDOMLinkPress',
string
> = makeFeature({
script: linkPressScript,
eventHandlerName: 'onDOMLinkPress',
identifier: 'org.webshell.linkPress'
});
The behavior in the DOM is implemented in the following file (please note that the extension is arbitrary, see the tooling section):
// src/features/link-press.webjs
function linkPressFeature(arg) {
var postMessage = arg.postMessage;
var options = arg.options || {};
var preventDefault = options.preventDefault !== false;
function findParent(tagname, el) {
while (el) {
if ((el.nodeName || el.tagName).toLowerCase() === tagname.toLowerCase()) {
return el;
}
el = el.parentNode;
}
return null;
}
function interceptClickEvent(e) {
var href;
var target = e.target || e.srcElement;
var anchor = findParent('a', target);
if (anchor) {
href = anchor.getAttribute('href');
preventDefault && e.preventDefault();
postMessage(href);
}
}
document.addEventListener('click', interceptClickEvent);
}
Every DOM script top declaration must be a function taking one argument. The shape of this argument is depicted in WebjsContext definition. For wide compatibility purposes, it is recommended to
See the tooling section for more details on how to achieve this.
The API reference is build with the amazing @microsoft/api-extractor utility. Read the API here: docs/webshell.md.
Landmark exports:
The objectives of the setup are:
.webjs
) as strings;To import .webjs
files as strings, we will use
babel-plugin-inline-import
with webjs
or whichever extension you are using for your WebView
scripts,
see babel.config.js file. This plugin will allow you to
import scripts as strings instead of compiling the module!
You can use
@formidable-webview/eslint-config-webjs
to target .webjs
files with specific config:
WebView
backends.To test injected scripts, the easiest way is to use
@formidable-webview/ersatz
.
Best to show with an example of our link-press DOM feature testing:
:warning: Note that you will need to replace relative imports with direct imports from the library,
import {...} from "@formidable-webview/webshell";
// src/features/__tests__/link-press.test.tsx
import * as React from 'react';
import Ersatz from '@formidable-webview/ersatz';
import makeErsatzTesting from '@formidable-webview/ersatz-testing';
import { render } from '@testing-library/react-native';
import { makeWebshell } from '../../make-webshell';
import { linkPressFeature } from '../link-press';
const { waitForDocument } = makeErsatzTesting(Ersatz);
describe('Webshell with linkPressFeature', () => {
it('should invoke onDOMLinkPress prop when a link is pressed', async () => {
const onDOMLinkPress = jest.fn();
const Webshell = makeWebshell(Ersatz, linkPressFeature.assemble());
const document = await waitForDocument(
render(
<Webshell
onDOMLinkPress={onDOMLinkPress}
source={{ html: '<a id="anchor0" href="https://foo.org">bar</a>' }}
/>
)
);
document.getElementById('anchor0').click();
expect(onDOMLinkPress).toHaveBeenCalledWith('https://foo.org');
});
});
The last thing you need to do is associate Javascript syntax with the custom extension you have chosen in your text editor. From now-on, you will have a full featured QC in those scripts!
Finally, if you want Github to provide syntax highlight to you webjs files, see our .gitattributes file.
FAQs
🔥 Craft Robust React Native WebView-based components with ease.
The npm package @formidable-webview/webshell receives a total of 7,484 weekly downloads. As such, @formidable-webview/webshell popularity was classified as popular.
We found that @formidable-webview/webshell demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.