Filter Search Panel Block
The FilterSearchPanel Component is a fully customizable and accessible search interface with filtering capabilities built with React and TypeScript. It provides a complete search and filter experience with modern design patterns, form validation, and flexible customization options.
π Installationβ
npm install @nodeblocks/frontend-filter-search-panel-block
π Usageβ
import {FilterSearchPanel} from '@nodeblocks/frontend-filter-search-panel-block';
- Basic Usage
- Advanced Usage
function BasicFilterSearchPanel() { const [filters, setFilters] = useState([ { label: 'Active', key: 'status-active', groupName: 'Status' }, { label: 'Web Development', key: 'category-web', groupName: 'Category' } ]); const handleRemoveFilter = (filter: FilterChip) => { setFilters(filters.filter(f => f.key !== filter.key)); }; const handleSearch = ({search}: {search: string}) => { console.log('Search submitted:', search); setFilters(prev => [...prev, {label: search, key: search, groupName: 'Custom'}]); }; return ( <FilterSearchPanel filters={filters} onClickRemoveFilter={handleRemoveFilter} onClickFilterButton={() => console.log('Filter button clicked')} onSearch={handleSearch} searchPlaceholder="Search services..." noFilterText="No filters applied" filterLabel="Filter Settings"> <FilterSearchPanel.SearchInput /> <FilterSearchPanel.ActionGroup /> </FilterSearchPanel> ); }
function AdvancedFilterSearchPanel() { const [filters, setFilters] = useState([ {label: 'Frontend', key: 'tech-frontend', groupName: 'Technology'}, {label: 'React', key: 'framework-react', groupName: 'Framework'}, {label: 'Senior', key: 'level-senior', groupName: 'Level'}, ]); const [searchHistory, setSearchHistory] = useState(['react developer', 'nodejs backend', 'fullstack engineer']); const handleRemoveFilter = (filter: {key: string}) => { setFilters(filters.filter(f => f.key !== filter.key)); }; const handleSearch = ({search}: {search: string}) => { console.log('Search submitted:', search); if (search.trim()) { setSearchHistory(prev => [search, ...prev.slice(0, 4)]); setFilters(prev => [...prev, {label: search, key: search, groupName: 'Search'}]); } }; return ( <FilterSearchPanel filters={filters} onClickRemoveFilter={handleRemoveFilter} onClickFilterButton={() => console.log('Advanced filter panel opened')} onSearch={handleSearch} searchPlaceholder="Search for developers..." noFilterText="π No filters applied - showing all results" filterLabel="π― Advanced Filters" style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', borderRadius: '12px', padding: '24px', boxShadow: '0 8px 32px rgba(0,0,0,0.1)', }} > {({defaultBlocks, defaultBlockOrder}) => { const searchInput = ( <div style={{position: 'relative'}}> {defaultBlocks.searchInput} {searchHistory.length > 0 && ( <div style={{ top: '100%', left: 0, right: 0, background: 'white', borderRadius: '8px', marginTop: '8px', padding: '12px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', zIndex: 10, }} > <div style={{fontSize: '12px', color: '#666', marginBottom: '8px'}}>π Recent searches:</div> {searchHistory.map((term, index) => ( <div key={index} style={{ padding: '4px 8px', fontSize: '14px', color: '#333', cursor: 'pointer', borderRadius: '4px', }} > {term} </div> ))} </div> )} </div> ); const actionGroup = ( <div style={{ background: 'rgba(255,255,255,0.9)', borderRadius: '12px', padding: '16px', marginTop: '16px', }} > {defaultBlocks.actionGroup} </div> ); const searchStats = ( <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px', background: 'rgba(255,255,255,0.1)', borderRadius: '8px', marginTop: '12px', color: 'white', }} > <span style={{fontSize: '14px'}}>π {filters.length} active filters</span> <span style={{fontSize: '14px'}}>π― {Math.floor(Math.random() * 150) + 50} results found</span> </div> ); const quickActions = ( <div style={{ display: 'flex', gap: '8px', marginTop: '16px', flexWrap: 'wrap', }} > <button style={{ padding: '8px 16px', background: 'rgba(255,255,255,0.2)', border: 'none', borderRadius: '20px', color: 'white', fontSize: '14px', cursor: 'pointer', transition: 'all 0.3s ease', }} > π Reset All </button> <button style={{ padding: '8px 16px', background: 'rgba(255,255,255,0.2)', border: 'none', borderRadius: '20px', color: 'white', fontSize: '14px', cursor: 'pointer', transition: 'all 0.3s ease', }} > πΎ Save Search </button> <button style={{ padding: '8px 16px', background: 'rgba(255,255,255,0.2)', border: 'none', borderRadius: '20px', color: 'white', fontSize: '14px', cursor: 'pointer', transition: 'all 0.3s ease', }} > π Create Alert </button> </div> ); return { blocks: {searchInput, actionGroup, searchStats, quickActions}, blockOrder: ['searchStats', 'quickActions', 'searchInput', 'actionGroup'], }; }} </FilterSearchPanel> ); }
π§ Props Referenceβ
Main Component Propsβ
Prop | Type | Default | Description |
---|---|---|---|
filters | FilterChip[] | [] | Array of currently selected filter chips to display |
defaultSearchValue | string | undefined | Default value for the search input field |
onClickRemoveFilter | (filter: FilterChip) => void | undefined | Callback function triggered when a filter chip is removed |
onClickFilterButton | () => void | undefined | Callback function triggered when the filter button is clicked |
onSearchChange | (event: HTMLInputElement) => void | undefined | Callback function triggered when search input value changes |
onSearch | (data: T) => void | undefined | Callback function triggered when form is submitted with search data |
searchPlaceholder | string | undefined | Placeholder text for the search input field |
noFilterText | string | undefined | Text displayed when no filters are selected |
filterLabel | string | undefined | Label text for the filter button |
className | string | undefined | Additional CSS class name for styling the form container |
children | BlocksOverride | undefined | Custom block components to override default rendering |
Note: This component inherits all props from the HTML form
element.
Sub-Componentsβ
The FilterSearchPanel component provides several sub-components. All sub-components receive their default values from the main component's context and can override these values through props.
FilterSearchPanel.SearchInputβ
Prop | Type | Default | Description |
---|---|---|---|
searchPlaceholder | string | From context | Placeholder text shown in the search input |
onSearchChange | (event: HTMLInputElement) => void | undefined | Callback function triggered when input value changes |
onSearch | () => void | undefined | Callback function triggered when search is performed |
defaultSearchValue | string | undefined | Default value for the search input |
className | string | undefined | Additional CSS class name for styling |
Note: This component inherits all props from the HTML div
element.
FilterSearchPanel.FilterButtonβ
Prop | Type | Default | Description |
---|---|---|---|
filterLabel | string | From context | Label text displayed on the filter button |
onClickFilterButton | () => void | From context | Callback function triggered when button is clicked |
className | string | undefined | Additional CSS class name for styling |
children | ReactNode | From context | Custom content to override default rendering |
Note: This component inherits all props from the HTML button
element.
FilterSearchPanel.SelectedFilterListβ
Prop | Type | Default | Description |
---|---|---|---|
filters | FilterChip[] | From context | Array of filter chips to display |
onClickRemoveFilter | (filter: FilterChip) => void | From context | Callback function triggered when a filter is removed |
noFilterText | string | From context | Text displayed when no filters are selected |
className | string | undefined | Additional CSS class name for styling |
Note: This component inherits all props from the HTML div
element.
FilterSearchPanel.FilterBadgeβ
Prop | Type | Default | Description |
---|---|---|---|
filter | FilterChip | Required | Filter chip object containing label, key, and groupName |
onClickRemoveFilter | (filter: FilterChip) => void | Required | Callback function triggered when the filter is removed |
className | string | undefined | Additional CSS class name for styling |
Note: This component inherits all props from the HTML div
element.
FilterSearchPanel.ActionGroupβ
Prop | Type | Default | Description |
---|---|---|---|
className | string | undefined | Additional CSS class name for styling |
Note: This component inherits all props from the HTML div
element.
π§ TypeScript Supportβ
Full TypeScript support with comprehensive type definitions:
import FilterSearchPanel from '@nodeblocks/frontend-filter-search-panel-block';
// Filter chip structure
interface FilterChip {
label: string;
key: string;
groupName: string;
}
// Default search form data structure
interface SearchFormData {
search: string;
}
// Extend with custom fields
interface CustomSearchFormData extends SearchFormData {
category?: string;
status?: string;
customField?: string;
}
const MyFilterSearchPanel = () => {
const [selectedFilters, setSelectedFilters] = useState<FilterChip[]>([
{ label: 'Active', key: 'status-active', groupName: 'Status' },
{ label: 'Premium', key: 'type-premium', groupName: 'Type' }
]);
const handleSearch = (formData: CustomSearchFormData) => {
console.log('Search submitted:', formData);
// Handle search submission
};
const handleRemoveFilter = (filter: FilterChip) => {
setSelectedFilters(prev => prev.filter(f => f.key !== filter.key));
};
const handleFilterButtonClick = () => {
console.log('Opening filter modal');
// Handle filter button click
};
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log('Search value changed:', event.target.value);
// Handle search input changes
};
return (
<FilterSearchPanel<CustomSearchFormData>
filters={selectedFilters}
onSearch={handleSearch}
onClickRemoveFilter={handleRemoveFilter}
onClickFilterButton={handleFilterButtonClick}
onSearchChange={handleSearchChange}
searchPlaceholder="Search for services..."
noFilterText="No filters applied"
filterLabel="Filter Options">
<FilterSearchPanel.SearchInput />
<FilterSearchPanel.ActionGroup />
</FilterSearchPanel>
);
};
Built with β€οΈ using React, TypeScript, and modern web standards.