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
optionsprop. 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
- Direct Data: If
optionsprop holds data → uses immediately and caches with encryption - Cache Check: Component checks localStorage for cached data (7-day expiry with encryption)
- Fetch Function: If cache expired/empty and
optionsis a function → calls function and caches results - 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
- Always type your state explicitly:
useState<IndustryCategory>(). - Import
type IndustryCategoryfor full type safety when handling the selected value. - Use optional chaining (
selectedIndustry?.name) and validate before form submission. - Pass an async function to
optionsfor lazy loading — the component handles caching internally. - Provide descriptive
placeholderandnoResultsTextto guide users (e.g., "Search for your industry").
Don’t
- Don't use untyped state
useState()— TypeScript will error when passing the setter toonSelectionChange. - Don't assume
valueexists, always handle theundefinedcase before accessing properties. - Don't fetch industry data on every render; use the async
optionsprop for efficient caching. - Don't use generic text like "Select..." or "Not found" — it provides poor user guidance.
- Don't skip form library integration (e.g., React Hook Form's
Controller) — you'll lose validation and error handling.