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 🙏

© 2026 – Pkg Stats / Ryan Hefner

bison-web-components

v2.0.0

Published

Bison JIB's suite of web components

Downloads

265

Readme

Operator Onboarding Web Component

A complete, self-contained web component for operator onboarding with 4-step stepper form, file uploads, validations, and success page.

Installation

<script src="https://cdn.jsdelivr.net/npm/[email protected]/component.js"></script>

Basic Usage

Minimal Setup (No Callbacks)

The simplest way to use the component:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/component.js"></script>
<operator-onboarding></operator-onboarding>

When submitted, form data is automatically logged to console and success page is shown.


Configuration

The component can be configured with optional attributes for API integration:

<operator-onboarding
  api-base-url="https://your-api-domain.com"
  embeddable-key="your-embeddable-key"
>
</operator-onboarding>

Attributes

| Attribute | Type | Default | Description | | ---------------- | -------- | ------------------------------------------------- | ---------------------------------------------------- | | api-base-url | String | https://bison-jib-development.azurewebsites.net | Base URL for API endpoints | | embeddable-key | String | Default key provided | Authentication key for API requests | | on-success | String | - | Name of global function to call on success | | on-error | String | - | Name of global function to call on error | | on-load | String | - | JSON string or global variable name for initial data |


Form Flow

The onboarding process consists of a 4-step stepper form:

  1. Business Details - Company information and address
  2. Representatives (Optional) - Add business representatives
  3. Bank Account - Link bank account details
  4. Underwriting - Upload required documents

Form Steps in Detail

Step 1: Business Details

  • Business name *
  • Doing Business As (DBA) *
  • EIN (Employer Identification Number) * (auto-formatted as XX-XXXXXXX)
  • Business website (auto-normalized to include https://)
  • Business phone * (auto-formatted as (555) 123-4567)
  • Business email *
  • Full address * (street, city, state, ZIP)

Step 2: Representatives (Optional)

  • Add/remove multiple representatives
  • Full CRUD interface
  • Each representative requires:
    • First name, last name *
    • Job title *
    • Phone * (auto-formatted)
    • Email *
    • Date of birth *
    • Full address * (street, city, state, ZIP)
  • Can skip entire step if no representatives to add

Step 3: Bank Account

  • Account holder name *
  • Account type * (checking/savings)
  • Routing number * (9 digits)
  • Account number * (4-17 digits)

Step 4: Underwriting

  • Upload supporting documents * (required)
  • Drag-and-drop or browse file selection
  • Maximum 10 files
  • Maximum 10MB per file
  • Accepted formats: PDF, JPG, JPEG, PNG, DOC, DOCX

Pre-populating Form Data

Method 1: Direct Property Assignment (Recommended)

const component = document.querySelector("operator-onboarding");

// Pre-populate with existing data
component.onLoad = {
  businessDetails: {
    businessName: "Acme Corp",
    doingBusinessAs: "Acme",
    ein: "12-3456789",
    businessWebsite: "https://acme.com",
    businessPhoneNumber: "5551234567",
    businessEmail: "[email protected]",
    BusinessAddress1: "123 Main St",
    businessCity: "San Francisco",
    businessState: "CA",
    businessPostalCode: "94105",
  },
  representatives: [
    {
      representativeFirstName: "John",
      representativeLastName: "Doe",
      representativeJobTitle: "CEO",
      representativePhone: "5559876543",
      representativeEmail: "[email protected]",
      representativeDateOfBirth: "1980-01-15",
      representativeAddress: "456 Oak Ave",
      representativeCity: "San Francisco",
      representativeState: "CA",
      representativeZip: "94105",
    },
  ],
  underwriting: {
    underwritingDocuments: [], // Can be pre-populated with File objects
  },
  bankDetails: {
    bankAccountHolderName: "Acme Corp",
    bankAccountType: "checking",
    bankRoutingNumber: "123456789",
    bankAccountNumber: "987654321",
  },
};

Method 2: HTML Attribute with JSON

<operator-onboarding
  on-load='{"businessDetails":{"businessName":"Acme Corp"}}'
></operator-onboarding>

Method 3: HTML Attribute with Global Variable

<script>
  const initialData = {
    businessDetails: {
      businessName: "Acme Corp",
      businessEmail: "[email protected]",
    },
  };
</script>

<operator-onboarding on-load="initialData"></operator-onboarding>

Advanced Usage with Callbacks

Method 1: Direct Property Assignment (Recommended for Frameworks)

Perfect for React, Vue, Angular, and vanilla JavaScript:

const component = document.querySelector("operator-onboarding");

// Success callback
component.onSuccess = (formData) => {
  console.log("Onboarding complete!", formData);

  // Send to your backend
  fetch("/api/operators/onboard", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(formData),
  });

  // Close your modal
  closeModal();
};

// Error callback
component.onError = (errorData) => {
  console.error("Onboarding error:", errorData);

  if (errorData.action === "resubmit") {
    // User clicked resubmit button
    console.log("User wants to retry submission");
  }
};

Method 2: HTML Attribute

Good for simple cases with global functions:

<operator-onboarding on-success="handleSuccess" on-error="handleError">
</operator-onboarding>

<script>
  function handleSuccess(data) {
    console.log("Success!", data);
    closeModal();
  }

  function handleError(errorData) {
    console.error("Error:", errorData);
  }
</script>

Method 3: Event Listeners

Listen to custom events:

// Success event
component.addEventListener("formComplete", (event) => {
  const formData = event.detail;
  console.log("Form completed!", formData);
  closeModal();
});

// Submission failure event
component.addEventListener("submissionFailed", (event) => {
  const errorData = event.detail;
  console.error("Submission failed:", errorData);
});

React Integration

Recommended Pattern (Using useRef)

import { useEffect, useRef } from "react";

function OnboardingModal({ isOpen, onClose }) {
  const componentRef = useRef(null);

  useEffect(() => {
    if (componentRef.current) {
      // Success handler
      componentRef.current.onSuccess = (data) => {
        console.log("Onboarding complete:", data);

        // Send to API
        fetch("/api/onboard", {
          method: "POST",
          body: JSON.stringify(data),
        });

        onClose();
      };

      // Error handler
      componentRef.current.onError = (errorData) => {
        console.error("Error:", errorData);
        // Handle errors appropriately
      };
    }
  }, [onClose]);

  if (!isOpen) return null;

  return (
    <div className="modal">
      <operator-onboarding
        ref={componentRef}
        api-base-url="https://your-api.com"
        embeddable-key="your-key"
      ></operator-onboarding>
    </div>
  );
}

With Pre-populated Data

function EditOperatorModal({ operatorId, isOpen, onClose }) {
  const componentRef = useRef(null);
  const [initialData, setInitialData] = useState(null);

  useEffect(() => {
    if (isOpen && operatorId) {
      // Fetch existing operator data
      fetch(`/api/operators/${operatorId}`)
        .then((res) => res.json())
        .then((data) => setInitialData(data));
    }
  }, [isOpen, operatorId]);

  useEffect(() => {
    if (componentRef.current) {
      // Pre-populate form
      if (initialData) {
        componentRef.current.onLoad = initialData;
      }

      // Set success handler
      componentRef.current.onSuccess = (updatedData) => {
        fetch(`/api/operators/${operatorId}`, {
          method: "PUT",
          body: JSON.stringify(updatedData),
        });
        onClose();
      };
    }
  }, [initialData, operatorId, onClose]);

  if (!isOpen) return null;

  return (
    <div className="modal">
      <operator-onboarding ref={componentRef} />
    </div>
  );
}

Data Structure

The component returns a complete data object:

{
  "businessDetails": {
    "businessName": "Acme Corp",
    "doingBusinessAs": "Acme",
    "ein": "12-3456789",
    "businessWebsite": "https://acme.com",
    "businessPhoneNumber": "(555) 123-4567",
    "businessEmail": "[email protected]",
    "BusinessAddress1": "123 Main St",
    "businessCity": "San Francisco",
    "businessState": "CA",
    "businessPostalCode": "94105"
  },
  "representatives": [
    {
      "id": "uuid-here",
      "representativeFirstName": "John",
      "representativeLastName": "Doe",
      "representativeJobTitle": "CEO",
      "representativePhone": "(555) 987-6543",
      "representativeEmail": "[email protected]",
      "representativeDateOfBirth": "1980-01-15",
      "representativeAddress": "456 Oak Ave",
      "representativeCity": "San Francisco",
      "representativeState": "CA",
      "representativeZip": "94105"
    }
  ],
  "underwriting": {
    "underwritingDocuments": [
      // Array of File objects
      File { name: "document.pdf", size: 1234567, type: "application/pdf" }
    ]
  },
  "bankDetails": {
    "bankAccountHolderName": "Acme Corp",
    "bankAccountType": "checking",
    "bankRoutingNumber": "123456789",
    "bankAccountNumber": "987654321"
  }
}

Features

4-Step Stepper Form - Visual progress indicator ✅ Field Validation - Real-time validation on blur
Auto-Formatting - Phone numbers, EIN, URLs
File Upload - Drag-and-drop with validation
CRUD Representatives - Add/remove multiple reps
Error Handling - Verification and submission failures
Success Page - Animated completion screen
Framework Friendly - Easy integration with React, Vue, etc.
Shadow DOM - Fully encapsulated styles
Zero Dependencies - Pure vanilla JavaScript
API Integration - Ready for backend integration


API Reference

Properties

| Property | Type | Description | | --------------- | ---------- | ------------------------------------------------------------------------------------------------------- | | onSuccess | Function | Callback function called when form is successfully submitted. Receives complete form data as parameter. | | onError | Function | Callback function called when submission fails. Receives error data as parameter. | | onLoad | Object | Pre-populate form fields with initial data. Accepts partial or complete form data object. | | apiBaseURL | String | Base URL for API endpoints. | | embeddableKey | String | Authentication key for API requests. |

Events

| Event | Detail | Description | | ------------------ | -------- | ---------------------------------------------------------------------------------------- | | formComplete | Object | Emitted when form is successfully submitted. event.detail contains complete form data. | | submissionFailed | Object | Emitted when form submission fails. event.detail contains error information. |

Global Functions

| Function | Parameters | Returns | Description | | ------------------------------------------- | ---------------------------------------------- | --------- | ----------------------------------- | | verifyOperator(operatorEmail, mockResult) | operatorEmail: string, mockResult: boolean | boolean | Verify if an operator email exists. |


Operator Verification

You can verify operator emails:

// Check if operator exists
const exists = verifyOperator("[email protected]", true);

if (exists) {
  // Proceed with operation
  console.log("Operator verified");
} else {
  // Show error
  console.error("Operator not found");
}

Error Handling

The component provides comprehensive error handling:

Submission Failures

When form submission fails:

component.addEventListener("submissionFailed", (event) => {
  const { formData, message, timestamp } = event.detail;
  console.error("Submission failed:", message);
  // Retry or show error
});

// Or use callback
component.onError = (errorData) => {
  if (errorData.action === "resubmit") {
    // User clicked resubmit button
    // Your retry logic here
  }
};

Modal Integration Example

<div id="onboardingModal" class="modal">
  <div class="modal-content">
    <operator-onboarding
      on-success="closeOnboardingModal"
      on-error="handleOnboardingError"
    >
    </operator-onboarding>
  </div>
</div>

<script>
  function closeOnboardingModal(data) {
    console.log("Onboarding complete for:", data.businessDetails.businessName);

    // Close modal
    document.getElementById("onboardingModal").style.display = "none";

    // Send data to backend
    fetch("/api/onboarding", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
  }

  function handleOnboardingError(errorData) {
    console.error("Onboarding error:", errorData);
    // Show error notification
  }
</script>

File Upload Validation

The underwriting step includes file upload with the following restrictions:

  • Maximum files: 10
  • Maximum size per file: 10MB
  • Allowed formats: PDF, JPG, JPEG, PNG, DOC, DOCX
  • Validation: Real-time with error messages

Files are validated on both drag-and-drop and browse selection. Invalid files are rejected with clear error messages.


Browser Support

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Any browser supporting Custom Elements V1 and Shadow DOM

BisonJibPayAPI - Direct API Access

In addition to the web component, you can use the BisonJibPayAPI class directly for API integration without the UI. This is useful when you need to interact with the BisonJibPay API programmatically.

Installation & Import

The API class is automatically exported when you load the component:

// ES Module
import { BisonJibPayAPI } from "./component.js";

// Or access from window (script tag)
const BisonJibPayAPI = window.BisonJibPayAPI;

Basic Usage

// Create API instance
const api = new BisonJibPayAPI(
  "https://bison-jib-development.azurewebsites.net",
  "YOUR_EMBEDDABLE_KEY",
);

// Validate operator email
try {
  const result = await api.validateOperatorEmail(
    "[email protected]",
    "OP123456",
  );
  console.log("Operator email is valid:", result);
} catch (error) {
  console.error("Validation failed:", error);
}

// Register operator
const formData = new FormData();
formData.append("businessName", "Acme Corp");
formData.append("businessEmail", "[email protected]");
// ... add more fields

try {
  const result = await api.registerOperator(formData);
  console.log("Operator registered successfully:", result);
} catch (error) {
  console.error("Registration failed:", error);
}

API Methods

validateOperatorEmail(email, operatorId)

Validates an operator email address.

Parameters:

  • email (string) - The operator email address to validate
  • operatorId (string) - The operator ID to validate

Returns:

  • Promise resolving to the API response

Example:

const result = await api.validateOperatorEmail(
  "[email protected]",
  "OP123456",
);

registerOperator(formData)

Registers a new operator with complete form data.

Parameters:

  • formData (FormData) - FormData object containing all operator information

Returns:

  • Promise resolving to the API response

Example:

const formData = new FormData();
formData.append("businessName", "Acme Corp");
formData.append("businessEmail", "[email protected]");
formData.append("ein", "12-3456789");
// ... add all required fields

const result = await api.registerOperator(formData);

React Integration Example

import { useEffect, useState } from "react";
import { BisonJibPayAPI } from "./component.js";

function EmailValidator() {
  const [api] = useState(
    () =>
      new BisonJibPayAPI(
        "https://bison-jib-development.azurewebsites.net",
        "YOUR_KEY",
      ),
  );
  const [email, setEmail] = useState("");
  const [operatorId, setOperatorId] = useState("");
  const [isValid, setIsValid] = useState(null);
  const [isLoading, setIsLoading] = useState(false);

  const validateEmail = async () => {
    setIsLoading(true);
    try {
      await api.validateOperatorEmail(email, operatorId);
      setIsValid(true);
    } catch (error) {
      setIsValid(false);
      console.error("Validation failed:", error);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div>
      <input
        type="text"
        value={operatorId}
        onChange={(e) => setOperatorId(e.target.value)}
        placeholder="Enter operator ID"
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter operator email"
      />
      <button onClick={validateEmail} disabled={isLoading}>
        {isLoading ? "Validating..." : "Validate"}
      </button>
      {isValid !== null && <p>{isValid ? "✓ Valid" : "✗ Invalid"}</p>}
    </div>
  );
}

Error Handling

The API methods throw structured errors that you can catch:

try {
  await api.validateOperatorEmail("[email protected]", "OP123456");
} catch (error) {
  console.error("Status:", error.status);
  console.error("Message:", error.data.message);
  console.error("Errors:", error.data.errors);
}

Error structure:

{
  status: 400,  // HTTP status code
  data: {
    success: false,
    message: "Validation failed",
    errors: ["Email does not exist in our system"]
  }
}

Using with Different Environments

// Development
const devApi = new BisonJibPayAPI(
  "https://bison-jib-development.azurewebsites.net",
  "DEV_KEY",
);

// Production
const prodApi = new BisonJibPayAPI(
  "https://bison-jib-production.azurewebsites.net",
  "PROD_KEY",
);

// Use environment variables
const api = new BisonJibPayAPI(
  process.env.REACT_APP_API_URL,
  process.env.REACT_APP_EMBEDDABLE_KEY,
);

API Integration

The component is designed to work with the BisonJibPay API. Configure your endpoints:

<operator-onboarding
  api-base-url="https://your-api-domain.com"
  embeddable-key="your-embeddable-key"
>
</operator-onboarding>

API Endpoints Used

  • POST /api/embeddable/validate/operator-email - Validate operator email
  • POST /api/embeddable/operator-registration - Register operator with form data

License

MIT

Author

@kfajardo