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@0.2.3
π 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.