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

@opendevstack/ods-selector-widget

v0.0.1

Published

A flexible and customizable web component for displaying platform navigation and resource links

Readme

ODS Selector Widget

A flexible and customizable web component for displaying platform navigation and resource links. Built with Stencil, this component provides a clean interface for organizing and accessing multiple platform resources in a project-based context.

Features

  • 🎨 Fully Customizable: Extensive CSS custom properties for theming
  • 🔌 Framework Agnostic: Works both with Angular and React
  • 🔄 Dynamic Content: Fetches platform data from a service URL
  • 🦴 Loading States: Built-in skeleton loader and error handling
  • 🔗 Platform Selector: Dedicated platform icons with enable/disable states
  • 📑 Sectioned Layout: Organize links into logical sections with tooltips
  • 🔀 Multiple Link Types: Support for platform, project (internal), and external links with appropriate icons

Table of Contents

Installation

NPM

npm install @opendevstack/ods-selector-widget

Usage

Angular

1. Import the Component

1. Import the Component

For Angular with NgModule (traditional setup):

In your app.module.ts:

import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { defineCustomElements } from '@opendevstack/ods-selector-widget/loader';

import { AppComponent } from './app.component';

defineCustomElements(window);

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }

For Angular with Standalone Components:

In your main.ts:

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { defineCustomElements } from '@opendevstack/ods-selector-widget/loader';

defineCustomElements(window);

bootstrapApplication(AppComponent, {
  providers: []
});

In your app.component.ts:

import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  // Component logic
}

2. Configure Angular (if needed)

Note: If you encounter the error ✘ [ERROR] No loader is configured for ".map" files, you need to update your angular.json configuration.

Add the following to your architect.build.options in angular.json:

{
  "architect": {
    "build": {
      "options": {
        "customWebpackConfig": {
          "loader": {
            ".map": "empty"
          }
        }
      }
    }
  }
}

3. Use in Template

<ods-selector-widget
  project="My Project"
  service-url="https://api.example.com/platforms/{{project}}">
</ods-selector-widget>

React

import React from 'react';
import { defineCustomElements } from '@opendevstack/ods-selector-widget/loader';

// Call this once in your app initialization
defineCustomElements(window);

function App() {
  return (
    <div>
      <ods-selector-widget
        project="My Project"
        service-url="https://api.example.com/platforms/${project}"
      />
    </div>
  );
}

export default App;

For better TypeScript support in React:

// Add to your declarations file (e.g., custom-elements.d.ts)
declare namespace JSX {
  interface IntrinsicElements {
    'ods-selector-widget': {
      project?: string;
      'service-url'?: string;
    };
  }
}

Configure React + Vite (if needed)

Note: If you encounter the error No loader is configured for ".map" files in a React + Vite project, update your vite.config.js or vite.config.ts to exclude .map files from dependency optimization.

Add the following to your Vite config:

// vite.config.js or vite.config.ts
export default {
  optimizeDeps: {
    exclude: ['*.map']
  }
}

Properties

| Property | Attribute | Type | Default | Description | |----------|-----------|------|---------|-------------| | project | project | string | undefined | The project name displayed in the widget header | | serviceUrl | service-url | string | undefined | The service URL to fetch platform data. Use ${project} as a placeholder that will be replaced with the project name |

Example

<ods-selector-widget
  project="PROJECT1"
  service-url="https://api.myapp.com/platforms/${project}">
</ods-selector-widget>

API Response Format

The component expects the service URL to return JSON data in the following format:

{
  "sections": [
    {
      "section": "Select your platform",
      "tooltip": "",
      "links": [
        {
          "label": "Platform One",
          "url": "https://platform1.com",
          "abbreviation": "P1",
          "type": "platform"
        },
        {
          "label": "Platform Two",
          "url": "https://platform2.com",
          "abbreviation": "P2",
          "type": "platform",
          "disabled": true
        },
        {
          "label": "Platform Three",
          "url": "https://platform3.com",
          "abbreviation": "P3",
          "type": "platform",
          "disabled": true
        }
      ]
    },
    {
      "section": "Documentation",
      "tooltip": "Access project documentation and guides",
      "links": [
        {
          "label": "API Reference",
          "url": "https://docs.example.com/api",
          "tooltip": "Complete API documentation",
          "type": "external"
        },
        {
          "label": "Getting Started",
          "url": "https://docs.example.com/getting-started",
          "tooltip": "Quick start guide",
          "type": "project"
        }
      ]
    },
    {
      "section": "Tools",
      "tooltip": "Development and monitoring tools",
      "links": [
        {
          "label": "Dashboard",
          "url": "https://dashboard.example.com",
          "tooltip": "Project dashboard",
          "type": "external"
        }
      ]
    }
  ]
}

API Response Schema

  • sections (array): Array of content sections to display. Each section can contain either platform links or regular resource links.
    • section (string): Section title
    • tooltip (string, optional): Tooltip text for the section header
    • links (array): Array of links in this section
      • label (string): Link display text
      • url (string): Link destination URL
      • type (string): Link type - "platform", "project", or "external"
        • "platform": Renders as platform selector icons (when all links in a section are platform type)
        • "project": Renders as list item with internal link arrow icon
        • "external": Renders as list item with external link squared arrow icon
      • tooltip (string, optional): Tooltip text for the link (shown on hover for non-platform links)
      • abbreviation (string, optional): Short text shown in platform icon (required for type: "platform")
      • disabled (boolean, optional): If true, the platform link is disabled and shown as non-clickable (only applicable for type: "platform")

Rendering Behavior

The component automatically adapts its rendering based on the link types within each section:

  • Platform Section: If all links in a section have type: "platform", they are rendered as clickable platform icons in a horizontal layout. This is typically used for the main platform selector section.

  • Link List Section: If a section contains any non-platform links (type: "project" or type: "external"), all links in that section are rendered as a vertical list with appropriate arrow icons split in two columns.

Example:

{
  "sections": [
    {
      "section": "Platforms",
      "links": [
        { "label": "Platform 1", "url": "...", "abbreviation": "P1", "type": "platform" },
        { "label": "Platform 2", "url": "...", "abbreviation": "P2", "type": "platform" }
      ]
    }
  ]
}

The example above renders as platform icons

{
  "sections": [
    {
      "section": "Resources",
      "links": [
        { "label": "Documentation", "url": "...", "type": "external" },
        { "label": "Dashboard", "url": "...", "type": "project" }
      ]
    }
  ]
}

The example above renders as a list with arrow/squared arrow icons.

Styling

The component uses CSS custom properties (CSS variables) for easy theming. You can override these in your stylesheet:

Available CSS Custom Properties

ods-selector-widget {
  /* Font family */
  --ods-selector-widget-font-family: 'Inter', sans-serif;

  /* Primary colors */
  --ods-selector-widget-color-primary: #08312A;
  --ods-selector-widget-color-white: #FFF;
  --ods-selector-widget-color-border: #5C5B59;
  --ods-selector-widget-color-shadow: rgba(0, 0, 0, 0.05);
  --ods-selector-widget-color-shadow-hover: rgba(0, 0, 0, 0.25);

  /* Header colors */
  --ods-selector-widget-header-bg: #CCFAE5;
  --ods-selector-widget-header-text: #08312A;

  /* Platform icon colors */
  --ods-selector-widget-platform-icon-bg: #08312A;
  --ods-selector-widget-platform-icon-text: white;
  --ods-selector-widget-first-platform-bg: #008E4C;
  --ods-selector-widget-first-platform-text: #ffffff;
  --ods-selector-widget-second-platform-bg: #5E71FF;
  --ods-selector-widget-second-platform-text: #ffffff;
  --ods-selector-widget-third-platform-bg: #00C86B;
  --ods-selector-widget-third-platform-text: #ffffff;

  /* Disabled state colors */
  --ods-selector-widget-disabled-bg: #E5E3DE;
  --ods-selector-widget-disabled-text: #898885;

  /* Section colors */
  --ods-selector-widget-section-text: #08312A;
  --ods-selector-widget-divider-color: #D3D3D3;

  /* Link colors */
  --ods-selector-widget-link-text: #08312A;
  --ods-selector-widget-link-hover: #6B8375;

  /* Error section colors */
  --ods-selector-widget-error-text: #051D19;
  --ods-selector-widget-error-button-border: #08312A;
  --ods-selector-widget-error-button-text: #08312A;
  --ods-selector-widget-error-button-hover-text: #6B8375;
  --ods-selector-widget-error-button-hover-border: #6B8375;

  /* Tooltip colors */
  --ods-selector-widget-tooltip-bg: #051D19;
  --ods-selector-widget-tooltip-text: #fff;

  /* Skeleton loader colors */
  --ods-selector-widget-skeleton-bg: #F6F5F3;
  --ods-selector-widget-skeleton-shimmer: #e4e3e2;
}

Example: Custom Theme

ods-selector-widget {
  --ods-selector-widget-font-family: "YourCustomFont", sans-serif;
  --ods-selector-widget-color-primary: #2563eb;
  --ods-selector-widget-header-bg: #dbeafe;
  --ods-selector-widget-header-text: #1e40af;
  --ods-selector-widget-first-platform-bg: #10b981;
  --ods-selector-widget-second-platform-bg: #8b5cf6;
  --ods-selector-widget-third-platform-bg: #f59e0b;
}

Development

Prerequisites

  • Node.js (v14 or higher)
  • npm (v6 or higher)

Setup

# Clone the repository
git clone https://github.com/opendevstack/ods-selector-widget.git
cd ods-selector-widget

# Install dependencies
npm install

Available Scripts

# Start development server with live reload
npm start

# Build for production
npm run build

# Run tests
npm test

# Run tests in watch mode
npm run test.watch

# Generate a new component
npm run generate

Project Structure

ods-selector-widget/
├── src/
│   ├── components/
│   │   └── ods-selector-widget/
│   │       ├── ods-selector-widget.tsx    # Component logic
│   │       ├── ods-selector-widget.css    # Component styles
│   │       ├── ods-selector-widget.spec.ts # Unit tests
│   │       └── ods-selector-widget.e2e.ts  # E2E tests
│   ├── index.html    # Demo page
│   └── index.ts      # Entry point
├── dist/             # Build output
├── www/              # Dev server output
└── stencil.config.ts # Stencil configuration

Testing

The component includes both unit and end-to-end tests:

# Run all tests
npm test

# Run only unit tests
npm run test -- --spec

# Run only e2e tests
npm run test -- --e2e

# Generate coverage report
npm test -- --coverage

Browser Support

This component works in all modern browsers that support:

  • Custom Elements V1
  • Shadow DOM
  • ES Modules

For older browsers, you may need to include polyfills.

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Contribution Guidelines

  • Write clear, descriptive commit messages
  • Add tests for new features
  • Update documentation as needed
  • Follow the existing code style
  • Ensure all tests pass before submitting PR

License

This project is licensed under the Apache License - see the LICENSE file for details.

Support

If you encounter any issues or have questions:

Acknowledgments

Built with Stencil - A toolchain for building reusable, scalable Design Systems.