Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@formidable-webview/webshell

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@formidable-webview/webshell

🚀 A Higher-order component to handle WebView DOM events in React Native

  • 0.10.1-alpha.4
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
9K
increased by16.32%
Maintainers
1
Weekly downloads
 
Created
Source

npm semver codecov CI

:warning: This library is in early development.

Webshell

Webshell is:

  • A Higher-order component to handle WebView DOM events in react-native. It provides an abstraction layer over the message system embedded in react-native-webview.
  • A set of utilities and good practices to implement well-tested, reliable scripts to run on the (WebView) DOM.

Here are examples of features shipped with this library:

  • Intercept click events on anchors and prevent navigation (linkPressFeature);
  • Get notified when an element size changes in the view (dimensionsFeature);

And you can, of course, implement any feature you'd like.

How does it work?

It uses WebView#injectedJavascript prop to inject javascript “features” (snippets) responsible for communicating information to the Webshell controller.

Install

npm install @formidable-webview/webshell

Basic usage

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 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,
  dimensionsFeature,
  DimensionsObject
} from '@formidable-webview/webshell';
import WebView from 'react-native-webview';

const Webshell = makeWebshell(
  WebView,
  linkPressFeature.assemble({ preventDefault: true }),
  dimensionsFeature.assemble({ tagName: 'table' })
);

export default function EnhancedWebView(webViewProps) {
  const onLinkPress = useCallback((url: string) => Linking.openURL(url), []);
  const onDimensions = useCallback(
    ({ width, height }: DimensionsObject) => console.info(width, height),
    []
  );
  const onShellError = useCallback(
    // featureIdentifier == linkPressFeature.identifier
    (featureIdentifier, errorMessage) => {
      if (featureIdentifier === linkPressFeature.identifier) {
        // Handle linkPress error
        console.error(errorMessage);
      } else if (featureIdentifier === dimensionsFeature.identifier) {
        // Handle dimensions error
        console.error(errorMessage);
      }
    },
    []
  );
  return (
    <Webshell
      onLinkPress={onLinkPress}
      onDimensions={onDimensions}
      onShellError={onShellError}
      webViewProps={webViewProps}
    />
  );
}

Implementing features

To have a good sense on how to implement 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 feature. There are four important areas to specify in a feature:

  1. How does it behave in the DOM? → See the content of linkPressScript.
  2. What options can it be assembled with? → LinkPressOptions
  3. What is the name of the event handler prop which will be available in the Webshell? → onLinkPress
  4. What type of payload does it ship with events? → string
// 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,
  'onLinkPress',
  string
> = makeFeature({
  script: linkPressScript,
  eventHandlerName: 'onLinkPress',
  identifier: 'org.webshell.linkPress'
});

:warning: Please note that you will need to replace import ... from.

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

  • enforce ECMAScript 5 syntax;
  • lint the script with the eslint compat plugin to enforce backward compatibility with old engines;
  • unit-test with jest jsdom environment;

See the tooling section for more details on how to achieve this.

API Reference

The API reference is build with the amazing @microsoft/api-extractor utility. Read the API here: doc/webshell.md.

Tooling

The original idea behind this project was to perform code quality control over scripts injected into WebViews, because they are parts of the codebase. To achieve that purpose, we are using babel and eslint original plugins.

babel

The most important thing is to use Babel 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!

eslint

We are using the overrides feature to target webjs files with specific options:

  • Enforce ECMAScript 5 to make sure it runs on reasonably old WebView backends.
  • Enforce a list of supported browsers with the outstanding compat plugin. We make sure we don't use recent web APIs without a fallback or polyfill.

See our .eslintrc.js file.

Jest

Jest will use our babel config file if babel-jest is installed. The most important aspect of our jest setup is that it has two configurations:

  • One to test our React Native code, which will use the react-native preset, core.jest.config.js;
  • One to test our DOM scripts, which will use the jsdom testing environment, web.jest.config.js.

To run jest with multiple configurations, you just need to set the projects attribute as an array of config files.

Editor

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!

Keywords

FAQs

Package last updated on 07 Aug 2020

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc