npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@paypal/web-component-utilities

v1.0.14

Published

## register Registers a custom web component from a preact component. This was borrowed from https://github.com/preactjs/preact-custom-element and altered to be more secure and fit our needs at PayPal.

Downloads

164

Readme

@paypal/web-component-utilities

register

Registers a custom web component from a preact component. This was borrowed from https://github.com/preactjs/preact-custom-element and altered to be more secure and fit our needs at PayPal.

Usage

Import @paypal/web-component-utilities and pass the preact component, tag name, list of attributes to observe and shadow DOM requirement. Only shadow DOM is supported with both open and closed modes.

import { register } from '@paypal/web-component-utilities';

const Email = ({ prefilled }) => {
  return (
    <>
      <input type='text' value={ prefilled } />
    </>
  );
};

register(Email, 'paypal-email', [ 'prefilled' ], { shadow: { mode: 'closed' } });

Note: The tag name in HTML must contain a hyphen

Use the tag name in HTML. Attribute keys and values will be passed in as props:

<paypal-email prefilled='[email protected]'></paypal-email>

Output:

<input type='text' value='[email protected]' />

Prop Names and Automatic Prop Names

The Custom Elements V1 specification requires explicitly stating the names of any attributes you want to observe. From your Preact component perspective, props could be an object with any keys at runtime, so it's not always clear which props should be accepted as attributes.

If you omit the third parameter to register(), the list of attributes to observe can be specified using a static observedAttributes property on your Component. This also works for the Custom Element's name, which can be specified using a tagName static property:

import { register } from '@paypal/web-component-utilities';

class Email extends Component {
  static tagName = 'paypal-email';
  static observedAttributes = [ 'prefilled' ];

  render({ prefilled }) {
    return (
      <>
        <input type='text' value={ prefilled } />
      </>
    );
  }
};

register(Email);

If no observedAttributes are specified, they will be inferred from the keys of propTypes if present on the Component:

import { register } from '@paypal/web-component-utilities';

const Email = (props) => {
  return (
    <>
      <input type='text' value={ props.prefilled } />
    </>
  );
};
Email.propTypes = {
  prefilled: PropTypes.string
};

register(Email, 'paypal-email');

State Management

Part of the register function is to keep the state between the preact and web component in sync. State flows from the web component to the preact component. Here are some ways to set and retrieve state from the web component.

Note: Attribute names should be hyphenated if camel-case in your preact component. When getting or setting the attribute, be sure to NOT use the camel-case representation.

Getting state

Web Component:

<paypal-email prefilled='[email protected]'></paypal-email>

Script:

document.querySelector('paypal-email').getAttribute('prefilled'); // [email protected]

or

document.querySelector('paypal-email').prefilled; // [email protected]

Note: Can only use the direct method above after DOM has loaded.

Preact: The getState method will retrieve state from sessionStorage if enabled.

import { getState } from '@paypal/web-component-utilities';
...
const prefilled = getState('prefilled');

Setting state

When setting the state of the web component, the preact component UI will be updated if the attribute that was changed was registered.

Web Component:

<paypal-email prefilled='[email protected]'></paypal-email>

Script:

document.querySelector('paypal-email').setAttribute('prefilled') = '[email protected]';

or

document.querySelector('paypal-email').prefilled = '[email protected]';

Note: Can only use the direct method above after DOM has loaded.

Preact: The setState method will set state to sessionStorage if enabled. Use this in your preact components when you need to persist state in case the page is refreshed. This method will also fire off and event that the property has changed. See Events section below on event nomenclature.

import { setState } from '@paypal/web-component-utilities';
...
setState('prefilled', '[email protected]');

Component communication

State passed to the Web Component

Component attribute -> Web Component

Setting a component attribute, sets its state for that attribute.

sessionStorage -> Web Component

sessionStorage is used on attributes that my require persistence across pages or if current page is refreshed. The merchant is allowed to also persist whatever state they need to for component intitialization. A web component will give precedence to attributes passed to it over the value stored in sessionStorage. This scenario can occur when a component's attribute is set from another component and not directly in source code. sessionStorage acts as a store in case user refreshes the page or their components are across multiple pages. If sessionStorage is disabled in the browser, then the user will experience the same kind of experience they do on other pages dependent on sessionStorage on other sites.

State passed from Web Component

Events

Events are used to send information to the merchant when they may need to take action or they need data from us for other reasons.

const paypalEmailComponent = document.querySelector('paypal-email');

paypalEmailComponent.addEventListener('OnAuth', e => {
  const { authToken } = e.detail || {};
});

Security

We have taken measures to secure the web component internals from being accessed using JavaScript and are checking for the override of attachShadow to prevent someone from changing the mode to open if the mode was set to closed. Also, when sending events to the merchant through the web component, we are preventing the event from bubbling outside of its boundaries.

Caution should be given whenever we store state in sessionStorage and make sure data stored is PCI compliant. Therefore, no inputted card information should be stored locally anywhere and only passed to graphql to acquire the required payment token for the merchant.

Components variable

All preact components can have access to all of the registered web components on the current page through the components export. Every component that is registered on the page is held in this closure variable. This allows components on the same page to send events privately to one another if needed, or to get the current state of those web components.

The components object is structured with the tag name as the key, and the DOM Element instance as the value.

import { components } from '@paypal/web-component-utilities';

const Email = () => {
  const shippingComponent = components['paypal-shipping'];
  const paymentComponent = components['payapl-payment'];

  const onLogin = (e) => {
    const { authToken } = e.detail || {};

    const onAuthEvent = new CustomEvent('onAuth', {
      bubbles: false,
      cancelable: true,
      detail: {
        authToken
      }
    });

    shippingComponent.dispatchEvent(onAuthEvent);
    paymentComponent.dispatchEvent(onAuthEvent);
  };
};

Shadows variable

All preact components can have access to all of the registered web component shadow roots on the current page through the shadows export. Every component that is registered on the page is held in this closure variable. This allows us access to the shadow root when needed, like for specifying the root container for style-components.

The components object is structured with the tag name as the key, and the DOM Element instance as the value.

import { shadows } from '@paypal/web-component-utilities';

const cache = createCache({
  key: 'pp-email',
  container: shadows['paypal-email']
});

Logging

The logging library supports a basic interface for the web components to interact with so that you can adapt any logger you are using by passing the instance into the Logger constructor. It utilizes the adapter pattern to adapt the web component interface with your specific logger interface by delegating to the passed logger. Methods not supported by the web component logger interface can be added at initialization.

Usage

Initialization

Import your logger and the web component Logger

import { Logger as BeaverLogger } from '@krakenjs/beaver-logger';
import { Logger, getLogger } from '@paypal/web-component-utilities';

Initialize your logger and pass into the web component Logger

const beaverLogger = BeaverLogger({
  url: '/api/log',
  enableSendBeacon: true
});

const logger = Logger({
  logger: beaverLogger
});
Initialize with custom event

By default the Logger supports the basic logging events: info, debug, warn, error. If your logger instance also supports these methods off of the instance, than no further configuration is necessary. If your logger supports other events, or your events have different names correlating to the above, then you can initialize the logger with those.

const logger = Logger({
  logger: beaverLogger,
  info: beaverLogger.information,
  track: beaverLogger.track
});

Log Event

Use the getLogger helper to access singleton logger instance.

Use the Logger in your web component:

getLogger().info('button_click', {
  sessionID: 'abc123'
});

If you've initialized a custom event, it will be available to use.

getLogger().track('button_click', {
  sessionID: 'abc123'
});