Skip to main content

GL Icon Set

Version: 1.1.0 Package: @gift-card-market/gl-icon-set
Last Updated: October 5, 2026

A React library (with Web Component support) for displaying a geolocated category icon set with customizable titles/descriptions and a "Buy Now" button connected to Gift Card Market.

Table of Contents​


Features​

  • Geolocated icon set: categories with normal/active icons, can fetch results based on user location.
  • Customizable titles, colors, subtitles (supports HTML in the title field).
  • Buy Now button with customizable text, color, and visibility.
  • Can pass pre-defined results (results) or request configuration for the library to call the API automatically.
  • Control API via ref (GlIconSetHandle) and via controller helpers (disableCategory, enableCategory, etc.).
  • Supports Web Component for embedding in non-React pages.
  • Written in TypeScript, built with tsup + Tailwind CSS.

Requirements​

  • React: ^18.0.0 or ^19.0.0.
  • Peer dependencies (install in your app):
npm install react react-dom react-slick slick-carousel @heroicons/react
  • Node / npm: follow the version used to build in the repo (not strictly required, but Node 18+ is recommended).

Installation​

1. Configure GitHub Packages (if package is still hosted on GitHub)​

From package.json, the package is published to GitHub Packages with:

"publishConfig": {
"registry": "https://npm.pkg.github.com/@Gift-Card-Market",
"access": "public"
}

You need to configure npm to read from this registry:

  1. Create a GitHub Personal Access Token with read:packages scope at: https://github.com/settings/tokens
  2. Login:
npm login --registry=https://npm.pkg.github.com --scope=@gift-card-market

Or add to your project's .npmrc file:

@gift-card-market:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN

2. Install the package​

npm install @gift-card-market/gl-icon-set

3. Import CSS​

// GL Icon Set styles
import '@gift-card-market/gl-icon-set/style.css';

// slick-carousel styles
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';

Quick Start with React​

Basic Example​

Category content is no longer provided by the partner — GlIconSet fetches its categories directly from the Admin Portal Icon Set API as soon as it mounts. Make sure the target environment has Icon Set categories configured in the Admin Portal, otherwise the component renders with an empty category list.

import '@gift-card-market/gl-dynamic-image/dist/style.css';
import { GlIconSet, type GlIconSetHandle } from '@gift-card-market/gl-icon-set';
import '@gift-card-market/gl-icon-set/style.css';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import { useRef } from 'react';

export default function IconSetPage() {
const ref = useRef<GlIconSetHandle | null>(null);

return (
<GlIconSet
ref={ref}
buttonConfig={{
buyNowText: 'Buy Now',
buyNowColor: '#ee3d84',
buyNowVisible: true,
themeColor: '#ee3d84',
buyNowTextColor: '#ffffff',
}}
maxCategories={8}
maxResultsPerCategory={8}
receiveUpdates={true}
environment={'production'}
jwt={YOUR_JWT_TOKEN}
/>
);
}

Using ref (GlIconSetHandle) for control​

function Toolbar() {
const ref = useRef<GlIconSetHandle | null>(null);

return (
<>
<button onClick={() => ref.current?.refresh()}>Refresh</button>
<button onClick={() => ref.current?.reset()}>Reset</button>
<button onClick={() => ref.current?.disableCategory('Restaurants')}>
Disable Restaurants
</button>
<button onClick={() => ref.current?.enableCategory('Restaurants')}>
Enable Restaurants
</button>
<button
onClick={async () => {
const results = await ref.current?.getResults();
console.log('Current results:', results);
}}
>
Log results
</button>

<GlIconSet ref={ref} jwt="YOUR_JWT_TOKEN" environment="production" />
</>
);
}

Detailed API​

Props GlIconSet​

The actual type is defined in glIconSetPros.ts. Summary:

PropTypeRequiredDescription
jwtstring✅JWT for calling Gift Card Market API. Access the following link to see instructions for obtaining JWT (access_token).
environment'production' | 'staging' | 'qa' | 'development'✅Backend environment; used to map to the corresponding domain.
buttonConfigButtonProps❌Customize Buy Now button and theme. If not provided, defaults will be used.
maxCategoriesnumber❌Maximum number of categories to display. Clamped to the range 1–8; default is 8. Values below 1 are raised to 1 and values above 8 are capped at 8.
maxResultsPerCategorynumber❌Maximum number of results per category (capped at 10). Default is 10.
receiveUpdatesboolean❌Whether to apply Admin Portal content overrides that are currently active. Default is true.

Note: category content is fetched automatically from the Admin Portal Icon Set API (the partner-supplied categories prop was removed in 1.0.8). If the number of fetched categories is greater than the effective maxCategories (1–8), the list is sliced.

CategoryConfig​

Defined in categoryConfig.ts (abbreviated):

export type BusinessInfo = {
name: string;
image: string;
};

export type CategoryConfig = {
id: string;
name: string;
iconUrl: string;
activeIconUrl: string;
title: string; // can contain HTML (see demo/main.tsx)
subtitle: string;
titleColor?: string;
subtitleColor?: string;
titleFont?: string;
subtitleFont?: string;
active?: boolean;
request?: {
term: string;
program: string;
};
results?: BusinessInfo[];
};

id is the Admin Portal category GUID (not partner-facing). Use name (e.g. 'Restaurants') when calling disableCategory/enableCategory via the ref API below.

Typical usage patterns:

  • With request: the library will automatically call the API to fetch results based on term and program.
  • With results: you provide a pre-defined list of businesses (gift cards, merchants, etc.).

ButtonProps (buttonConfig)​

In GlIconSet.tsx, there is a default configuration:

const configurationDefault = {
themeColor: '#0052cc',
buyNowText: 'Buy Now',
buyNowColor: '#0052cc',
buyNowTextColor: '#ffffff',
buyNowVisible: true,
};

The ButtonProps type is defined correspondingly in glIconSetPros.ts (summary):

  • buyNowVisible: boolean – show/hide the Buy Now button.
  • buyNowText: string – button text.
  • buyNowColor: string – button background color.
  • buyNowTextColor?: string – text color.
  • themeColor: string – main theme color of the component.

When you pass buttonConfig, any field not provided will use the default value.

GlIconSetHandle (ref API)​

Full definition in glIconSetHandle.ts. Main methods:

  • refresh(): void – refresh the current results/state.
  • reset(): void – reset to initial state (categories as initial).
  • disableCategory(categoryName: string): void – disable a category, matched by name (case-insensitive). If the disabled category is the one currently displayed, the component automatically selects and reloads the category that shifts into that position; if it was the last one, it falls back to the first category. Disabling a non-selected category keeps the current selection in sync.
  • enableCategory(categoryName: string): void – re-enable a category, matched by name (case-insensitive). The currently-selected category stays selected; if everything was disabled, the re-enabled category is selected and loaded.
  • addCategory(config: CategoryConfig): void – add a new category to the list.
  • removeCategory(categoryId: string): void – remove a category, matched by id.
  • getResults(): Promise<CategoryConfig[]> | undefined – get current results (async). Returns undefined if handle is not ready.
  • searchCategory(categoryId: string): Promise<CategoryConfig[]> | undefined – trigger search for a specific category, matched by id.

button-config is parsed without eval. Strict JSON is the recommended form; the object-literal form shown above (unquoted keys, single quotes) is still accepted. Anything that is not an object literal is ignored with a console warning.

IconSetEvents (Event bus)​

The Custom Element (gl-icon-set) uses Shadow DOM (mode: 'open') to completely encapsulate styling, preventing CSS bleeding. Component styles are compiled and injected directly into the shadow root during build execution.

Custom DOM Events are dispatched once, on the component root, and bubble up with composed: true, so a listener on the host element, on document or on window all receive them exactly once:

Every object payload carries an instanceId so two components on the same page can be told apart. gl:error is the exception: its event.detail is the plain error message.

Eventevent.detailFired when
gl:iconset-ready{ categoryCount, instanceId }The category configuration has been applied
gl:category-loaded{ categories, resultCount, instanceId }The category list is available
gl:category-clicked{ categoryId, categoryName, instanceId }A category tab is selected
gl:buynow-clicked{ businessId, cardProgram?, instanceId }The Buy Now button or a card image is clicked
gl:search-complete{ categories, totalResults, instanceId }A category finished loading, including when it returned no results
gl:location-acquired{ lat, lng, instanceId }The browser returned a position
gl:location-denied{ reason, message?, instanceId }No usable location. reason is permission-denied, position-unavailable, location-not-resolved or unsupported
gl:errorerror messageA request failed

When no location is available the component renders an empty state and raises gl:location-denied instead of staying blank. When a category search returns nothing it renders an empty state and raises gl:search-complete with totalResults: 0.

You can listen to these events directly on the Web Component instance (recommended):

const iconSet = document.querySelector('gl-icon-set');
iconSet.addEventListener('gl:category-clicked', (e) => {
console.log('Category changed on host element:', e.detail.categoryId);
});

Or globally on the document or window object:

document.addEventListener('gl:category-clicked', (e) => {
console.log('Global category changed event:', e.detail.categoryId);
});

Web Component Usage​

The package includes browser build support in this repository (src/web.ts, build).

Shadow DOM Isolation​

The Custom Element <gl-icon-set> uses Shadow DOM (mode: 'open') to completely encapsulate styling, preventing CSS bleeding. Component styles are compiled and injected directly into the shadow root during build execution.

The element can be moved around the page. On reconnect it reuses its existing shadow root and keeps rendering, so wrapping it in a tab, an accordion or a drag-and-drop layout is safe.

Embedding Pattern​

Include the global stylesheet and script in your HTML page:

<link rel="stylesheet" href="/path/to/@gift-card-market/gl-icon-set/dist/browser/gl-icon-set.css">
<script src="/path/to/@gift-card-market/gl-icon-set/dist/browser/gl-icon-set.global.js"></script>

Category content is no longer set via a categories attribute/property — the component fetches its categories directly from the Admin Portal Icon Set API as soon as it mounts. Just mount the tag with jwt/environment (and optionally max-categories/max-results-per-category/receive-updates):

<gl-icon-set
environment="development"
buttonConfig="{{ buyNowText: 'Buy Now', buyNowColor: '#ee3d84', buyNowVisible: true, themeColor: '#ee3d84', buyNowTextColor: '#ffffff' }}"
max-categories="8"
max-results-per-category="8"
receive-updates="true"
jwt="YOUR_JWT_TOKEN"
></gl-icon-set>

Scripting API​

Alternatively, you can assign other properties programmatically:

const iconSetEl = document.querySelector('gl-icon-set');

customElements.whenDefined('gl-icon-set').then(() => {
iconSetEl.maxCategories = 8;
iconSetEl.maxResultsPerCategory = 8;
iconSetEl.receiveUpdates = true;
iconSetEl.buttonConfig = {
buyNowText: 'Buy Now',
buyNowColor: '#ee3d84',
buyNowVisible: true,
themeColor: '#ee3d84',
buyNowTextColor: '#ffffff'
};
});

Stylesheets: which file goes where​

  • Web Component (dist/browser/gl-icon-set.global.js): add no stylesheet to the page. The styles are inlined into the shadow root, so nothing is needed at document level.
  • React: import @gift-card-market/gl-icon-set/style.css. Every rule in it is prefixed with .gl-is-scope, so importing it cannot change the width, margin or any other style of a host-page class such as Bootstrap's .container.
  • dist/browser/gl-icon-set.css and dist/web.css carry the same scoped rules for existing integrations and are not needed with the .global.js bundle. dist/index.css additionally @imports the slick stylesheets, which cannot be scoped at build time, so prefer style.css.
  • The only rule that stays at document level is the @font-face declaration for the slick arrow font, because browsers ignore @font-face inside a shadow root.

Changelog​

Version 1.1.0 (Current)​

  • 🆕 Empty states and gl:location-denied with a reason when there is no location or no results.
  • 🐛 No more Cannot set properties of undefined error while categories are still loading.
  • 🐛 gl:iconset-ready and gl:category-loaded fire once, after the categories are fetched.
  • 🔒 button-config is parsed without eval; stylesheets scoped to .gl-is-scope; buy-now color no longer written to <html>.
  • 📢 Object event payloads carry an instanceId; moving the element no longer throws; debug console.log calls removed.

Version 1.0.8​

  • 🧹 Removed the deprecated categories prop from the public type (GlIconSetPros). It has been ignored at runtime since 1.0.7 (content is fetched from the Admin Portal Icon Set API); it is now removed from the type entirely. No runtime impact — delete it from any remaining TypeScript usages.
  • 🔒 maxCategories is now clamped to the range 1–8 (minimum 1, maximum 8). Values below 1 are raised to 1 and values above 8 are capped at 8. The limit is applied to both the initial category list and runtime addCategory.
  • 🐛 Fixed category disable/enable selection behavior:
    • Disabling the currently-displayed category now auto-selects and reloads the category that shifts into that position; if the disabled one was the last, it falls back to the first category (index 0) instead of showing an empty content area.
    • Disabling a different (non-selected) category keeps the current selection, with the highlighted tab and the content below staying in sync after the list re-indexes.
    • Fixed a stale-reference bug so disableCategory/enableCategory invoked via the ref API or Web Component always act against the category that is currently selected.

Version 1.0.7​

  • 💥 Breaking (runtime): categories prop is now ignored — content is fetched automatically from the Admin Portal Icon Set API (GET /api/v1/web-component/icon-set) as soon as the component mounts. The prop is kept in the type as @deprecated for backward compatibility, but no longer has any effect. If your Icon Set categories aren't configured in the Admin Portal for the target environment, the component renders with an empty list.
  • 🆕 Added maxResultsPerCategory prop (and max-results-per-category attribute) — caps results per category, up to 10.
  • 🆕 Added receiveUpdates prop (and receive-updates attribute) — controls whether active Admin Portal content overrides are applied.
  • 🐛 Fixed disableCategory/enableCategory (ref API and Web Component) to be documented and typed as matching by category name (not id) — the Admin Portal-driven id is now a backend GUID, not partner-facing.
  • 🐛 Removed the unsupported 'local' value from the environment type.

Version 1.0.6​

  • 🔒 Encapsulated CSS bleeding fixes (scoped variables and nested selectors).
  • ⚙️ Added Tailwind utility prefix and configured important option.
  • 📐 Inline slick-carousel stylesheets in the styles bundle to fix absolute positioning of slider arrows in Shadow DOM.

Version 1.0.4​

  • 🚀 Enhanced custom element wrapper (GLIconSetComponent) to support passing properties directly via HTML attributes using JS expressions and string evaluation (e.g. buttonConfig="{{ buyNowText: 'Buy Now' }}", categories="data").

Version 1.0.3​

  • 🐛 Bug fixes and stability improvements.

Version 1.0.2​

  • 🐛 Bug fixes and stability improvements.