IndustryDropdown

An IndustryDropdown component provides a searchable interface for selecting business types. The component features intelligent search with normalized text matching, priority-based results, automatic caching with encryption, and responsive mobile sheet view with fixed height. It supports both direct data and async fetch functions, handles diacritics, and provides customizable popover settings for desktop and mobile experiences.

Quick Start

Installation
npm install @adaptavant/industry-dropdown
Import
import { IndustryDropdown } from '@adaptavant/industry-dropdown';

Key Features

A comprehensive industry selection widget with intelligent search and responsive behavior:

  • Combobox Integration - Built with EDS Combobox components for consistent user experience and full keyboard navigation. Provides accessible search with screen reader support and standard keyboard interactions.
  • Smart Caching System - Automatically caches industry data for 7 days with AES-GCM encryption, falling back seamlessly to default data when needed.
  • Intelligent Search - Prioritizes results starting with the search term, followed by matches anywhere in the name. Handles accented characters (ü → u, é → e) with normalized text matching.
  • Responsive Mobile View - Automatically switches to mobile sheet on small screens with a fixed height to prevent jumping during search, includes close button in header.
  • Controlled Component - Fully controlled with value/onSelectionChange pattern for predictable state management and seamless form integration.
  • Flexible Data Loading - Pass data directly or provide a fetch function. When using a fetch function, checks cache first and only calls your function when needed, optimizing API usage.
  • Consumer-Managed Data - Accepts industry data from consumer applications who handle API fetching and authentication. The widget focuses on presentation, caching, and search while keeping API credentials secure in the consumer app.
  • Custom Industry Types - Provide domain-specific industry lists via the options prop. Perfect for specialized applications that need custom industry categories tailored to their business needs.
  • Production Ready - Includes proper TypeScript support, encrypted caching, and comprehensive error handling for production environments.

Data Flow

  1. Direct Data: If options prop holds data → uses immediately and caches with encryption
  2. Cache Check: Component checks localStorage for cached data (7-day expiry with encryption)
  3. Fetch Function: If cache expired/empty and options is a function → calls function and caches results
  4. Default Data: If nothing else available → uses built-in default industries

Basic Usage

Simple setup with default behavior. The component automatically handles mobile/desktop differences, caching, and search prioritization. The searchFallbackText prop displays a prompt on mobile before the user starts typing. On desktop, it's automatically hidden to show all options immediately.

const [selectedIndustry, setSelectedIndustry] = React.useState();

return (
  <Field label="Industry">
    <IndustryDropdown
      placeholder="Search..."
      noResultsText="No search result"
      searchFallbackText="Search to view industry results"
      value={selectedIndustry}
      onSelectionChange={setSelectedIndustry}
    />
  </Field>
);

With Custom Industry Types

Provide domain-specific industry categories using the options prop. Data is automatically cached with encryption for 7 days.

const customIndustries = [
  { id: '1', name: 'Automotive' },
  { id: '2', name: 'Web Design'},
  { id: '3', name: 'Beauty'},
  { id: '4', name: 'Café'},
  { id: '5', name: 'Cybersecurity'},
  { id: '6', name: 'Dentist'},
  { id: '7', name: 'Photography'},
  { id: '8', name: 'Tütor'}
];

const [selectedIndustry, setSelectedIndustry] = React.useState();

return (
  <Field label="Select Your Industry">
    <IndustryDropdown
      options={customIndustries}
      noResultsText="No matching business types found"
      placeholder="Find your business type..."
      value={selectedIndustry}
      onSelectionChange={setSelectedIndustry}
    />
  </Field>
);

With Cached Data

The consumer application is responsible for fetching data from their API and passing it to the widget. The widget handles caching with encryption. Component checks localStorage for cached data (7-day expiry with encryption). If the cache is not present, it takes data from the options prop → uses immediately and caches with encryption.

const [industries, setIndustries] = React.useState([]);
const [selectedIndustry, setSelectedIndustry] = React.useState();

React.useEffect(() => {
  // Fetch from your API endpoint
  const fetchIndustries = async () => {
    const response = await fetch('/api/industries');
    const data = await response.json();
    setIndustries(data);
  };
  
  fetchIndustries();
}, []);

return (
  <Field label="Select Your Industry">
    <IndustryDropdown
      options={industries}
      noResultsText="No matching industries found"
      placeholder="Find your business type..."
      value={selectedIndustry}
      onSelectionChange={setSelectedIndustry}
    />
  </Field>
);

With Fetch Function

Pass a fetch function to the options prop. The component intelligently checks cache first and only calls your function when cache is expired or empty.

const [selectedIndustry, setSelectedIndustry] = React.useState();

return (
  <Field label="Select Your Industry">
    <IndustryDropdown
      options={async () => {
        const response = await fetch('/api/industries');
        return response.json();
      }}
      noResultsText="No industries found"
      placeholder="Search industries..."
      value={selectedIndustry}
      onSelectionChange={setSelectedIndustry}
    />
  </Field>
);

With Preselected Value

const [selectedIndustry, setSelectedIndustry] = React.useState({
  id: '2',
  name: 'Web Design',
});

return (
  <Field label="Industry">
    <IndustryDropdown
      noResultsText="No search result"
      placeholder="Search..."
      value={selectedIndustry}
      onSelectionChange={setSelectedIndustry}
    />
  </Field>
);

Search Functionality with Instructions

The component supports normalized search that handles accented characters:

  • Type "den" → finds "Dentist"
  • Type "beau" → finds "Beauty"
  • Type "tutor" → finds "Tütor" (accent normalized)
  • Type "cafe" → finds "Café" (accent normalized)
const [selectedIndustry, setSelectedIndustry] = React.useState();

return (
  <Field label="Industry">
    <IndustryDropdown
      noResultsText="No matching industries"
      placeholder="Try searching for: den, beau, tutor, cafe..."
      value={selectedIndustry}
      onSelectionChange={setSelectedIndustry}
    />
  </Field>
);

Style API

Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.

Please refer to the Style API documentation for more insights.

IndustryDropdown parts

The IndustryDropdown component provides styling customization through popover props and supports EDS Field styling patterns.

const [selectedIndustry, setSelectedIndustry] = React.useState();

return (
  <Field 
  label="Industry"
  errorMessage="Select a industry"
      classNames={{
        label: 'text-critical',
      }}
      >
    <IndustryDropdown
      placeholder="Search..."
      noResultsText="No industries found"
      searchFallbackText="Start typing to search"
      value={selectedIndustry}
      onSelectionChange={setSelectedIndustry}
      className="w-72"
      popoverMatchReferenceWidth={false}
      popoverMaxHeight={400}
      popoverMaxWidth={500}
      popoverPlacement="bottom-start"
    />
  </Field>
);

Best practices

Do

  1. Always type your state explicitly: useState<IndustryCategory>().
  2. Import type IndustryCategory for full type safety when handling the selected value.
  3. Use optional chaining (selectedIndustry?.name) and validate before form submission.
  4. Pass an async function to options for lazy loading — the component handles caching internally.
  5. Provide descriptive placeholder and noResultsText to guide users (e.g., "Search for your industry").

Don’t

  1. Don't use untyped state useState() — TypeScript will error when passing the setter to onSelectionChange.
  2. Don't assume value exists, always handle the undefined case before accessing properties.
  3. Don't fetch industry data on every render; use the async options prop for efficient caching.
  4. Don't use generic text like "Select..." or "Not found" — it provides poor user guidance.
  5. Don't skip form library integration (e.g., React Hook Form's Controller) — you'll lose validation and error handling.