JSPM

  • Created
  • Published
  • Downloads 320
  • Score
    100M100P100Q96670F
  • License MIT

Reusable React component library for the iGamingCareer platform

Package Exports

  • @igamingcareer/igaming-components

Readme

iGaming Components

A reusable React component library for iGamingCareer that can be consumed from React apps or embedded in plain HTML via data attributes. The project is built with Vite and ships Tailwind-powered styles.

Local development

  1. Install dependencies (Node 16+ is recommended):

    npm install
  2. Start the Vite dev server:

    npm run dev
  3. Build the distributable assets:

    npm run build
    # or generate the library bundle and type declarations
    npm run build:lib

Storybook

Storybook is configured with the Vite builder to explore components in isolation.

  • Start Storybook locally:

    npm run storybook
  • Generate the static Storybook site:

    npm run build-storybook

Using the library in React

Install the package from npm (or link the local build), then import the components and shared styles:

npm install @igamingcareer/igaming-components
import {
  Button,
  JobListings,
  ConsentBanner,
  HomePage,
} from "@igamingcareer/igaming-components";
import "@igamingcareer/igaming-components/styles/globals.css";

export function Example() {
  return (
    <div>
      <Button dataName="Apply now" />
      <JobListings />
      <ConsentBanner />
      <HomePage />
    </div>
  );
}

Sign-in prompt modal for gated actions

Use the SignInPromptModal to nudge unauthenticated users to log in or create an account before performing actions such as saving a job or opening a full job description. The parent app controls when the modal opens and owns the navigation/flows via the emitted callbacks.

import { useState } from "react";
import { SignInPromptModal } from "@igamingcareer/igaming-components";

export function GatedSaveButton() {
  const [open, setOpen] = useState(false);

  const handleLogin = () => {
    setOpen(false);
    // trigger your app's login flow
  };

  const handleRegister = () => {
    setOpen(false);
    // trigger your app's register flow
  };

  return (
    <>
      <button onClick={() => setOpen(true)}>Save job</button>

      <SignInPromptModal
        open={open}
        onClose={() => setOpen(false)}
        onLogin={handleLogin}
        onRegister={handleRegister}
        appName="iGamingCareer.com"
        attemptedAction="save this job"
        helperContent={<p className="text-sm text-muted-foreground">Create an account to sync saves across devices.</p>}
      />
    </>
  );
}

Embedding components in plain HTML

The library can hydrate elements that carry specific classes or data attributes. Build the project, host the dist/ output (or publish it to a CDN), and reference the emitted CSS/JS assets from your page.

  1. Run npm run build and copy the dist/ assets to your static host.

  2. Add the generated stylesheet and script tags from dist/index.html (the file names include hashes). Example:

    <link rel="stylesheet" href="/assets/index-XXXX.css" />
    <script type="module" src="/assets/index-XXXX.js"></script>
  3. Mark up the page with the hooks expected by the components (see src/main.tsx for the full list). For example:

    <div id="app"></div>
    
    <div class="button-component" data-name="Join now"></div>
    
    <div class="modal-component" id="offer-modal">
      <div class="title">Limited offer</div>
      <div class="description">Save 20% when you enroll today.</div>
      <div class="footer">Terms and conditions apply.</div>
    </div>

    When the bundle runs it will automatically hydrate these elements:

    • #app renders the root <App /> component.
    • .button-component renders the <Button /> component using the data-name attribute as the label.
    • .modal-component renders a <CustomModal /> instance, reading nested .title, .description, and .footer content.
  4. Use additional class hooks such as .group-prices-component, .hero-component, .courses-component, .chatbot-component, .faq-component, .videos-component, .testimonials-component, and .sliding-summary-component to hydrate the corresponding widgets. Pass data through data-* attributes as illustrated in src/main.tsx.

This approach allows teams that are not using React to embed the library’s UI on any HTML page while still benefiting from the React components under the hood.

Interaction events (Listing, Courses, News)

The Listing, IGamingCoursePage, and NewsPage components can now emit structured interaction events for analytics or logging. Supply an onEmitEvent callback and optional eventContext when you render the component (React or DOM embedding):

import { Listing } from "@igamingcareer/igaming-components"
import type { EmittedEvent } from "@igamingcareer/igaming-components"

const handleEvent = (event: EmittedEvent) => {
  console.log("component event", event)
  // Forward to your analytics/datastore
}

<Listing
  apiUrl="/api/jobs"
  filterKeys={["company", "city"]}
  cardType="job"
  onEmitEvent={handleEvent}
  eventContext={{ route: "/jobs", source: "listing" }}
/>

All emitted events share the base shape { domain, name, payload, timestamp, context } (see src/types/events.ts). Domain-specific unions live in src/types/events.jobs.ts, src/types/events.courses.ts, and src/types/events.news.ts.

Key events include:

  • Jobs Listing: search submissions/removals, filter select/clear/all-clear, date changes, pagination, view mode, items per page, job open/save/apply, alerts/subscription steps.
  • Courses Listing: search submissions, filter changes/clear, category/subcategory selection, view mode, pagination, items per page, favorites, enroll/open clicks.
  • News Listing: search submissions/removals, filter select/clear/all-clear, date changes, category selection, pagination, view mode, items per page, article open/bookmark/share.

When embedding via renderMultiple (see src/main.tsx), a global window.IGC_EMIT_EVENT handler will be passed automatically if present, along with a default context using the current route and optional data-source attribute.

Job Listing pagination-friendly API responses

The <Listing /> component now understands paginated API payloads. Set apiProvidesPagination (or the data-api-pagination="true" attribute when embedding) to tell the component your endpoint returns a { jobs: [], pagination: { page, limit, total, total_pages } } envelope.

  • The component requests page 1 with limit set to the initial itemsPerPage value, reads the pagination.total/total_pages metadata, and then sequentially loads every remaining page to build the full jobs[] in memory.
  • While those pages are fetched, the UI stays in the loading state (using the first batch size for the skeleton), and once all pages are collected it serves the same client-side pagination experience as array-based responses.
  • If the flag is omitted, the component assumes a plain array response and paginates client-side as before.

Example response shape:

{
  "jobs": [/* job objects for the current page */],
  "pagination": {
    "page": 1,
    "limit": 5,
    "total": 2284,
    "total_pages": 457
  },
  "metadata": {
    "cache_hit": false
  }
}

If your API still returns a plain array, leave apiProvidesPagination unset and the component will paginate client-side as before.