Skip to main content

Filter Search Panel Block

FilterSearchPanel is a search-and-filter toolbar built on MUI with a submit search field, filter settings button, and removable filter chips.

Installation​

npm install @nodeblocks/frontend-filter-search-panel-block@0.4.1

What You Need​

ItemWhy it matters
filtersActive filter chips shown in the panel ({ label, key, groupName? })
defaultSearchValueInitial value for the search field
onSearchCalled on form submit with parsed form data (default field: search)
onSearchChangeReceives search input change events when you want controlled search
onClickRemoveFilterRemoves a chip when the user deletes it
onClickFilterButtonOpens your filter UI (modal, drawer, route, etc.)
searchPlaceholderPlaceholder text used by SearchInput
noFilterTextText shown when no filters are selected
filterLabelLabel shown on the filter settings button
Parent-owned filter state

FilterSearchPanel does not own the filters array. Keep chips in your app state and update them in onClickRemoveFilter (and when applying filters elsewhere).

Code Examples​

Live Editor
function Example() {
  const defaultFilters = [
    {label: 'Active', key: 'status-active'},
    {label: 'Inactive', key: 'status-inactive'},
  ];

  const filterCatalog = [
    {label: 'Active', key: 'status-active'},
    {label: 'Inactive', key: 'status-inactive'},
    {label: 'Remote', key: 'work-remote', groupName: 'Work type'},
    {label: 'Full-time', key: 'employment-full-time', groupName: 'Employment'},
    {label: 'Contract', key: 'employment-contract', groupName: 'Employment'},
  ];

  const [filters, setFilters] = React.useState(defaultFilters);
  const handleRemoveFilter = filter => {
    setFilters(current => current.filter(f => f.key !== filter.key));
  };

  const handleFilterButton = () => {
    const nextFilter = filterCatalog.find(option => !filters.some(f => f.key === option.key));

    if (nextFilter) {
      setFilters(current => [...current, nextFilter]);
    }
  };

  const handleSearch = ({search}) => {
    if (!search?.trim()) {
      return;
    }

    setFilters(current => [...current, {label: search, key: `search-${Date.now()}`, groupName: 'Search'}]);
  };

  return (
    <FilterSearchPanel
      filters={filters}
      searchPlaceholder="Search services..."
      noFilterText="No filters applied"
      filterLabel="Filter Settings"
      onClickFilterButton={handleFilterButton}
      onClickRemoveFilter={handleRemoveFilter}
      onSearch={handleSearch}
    />
  );
}
Result
Loading...

Important Props​

Core Props​

PropTypeRequiredDefaultDescription
filtersFilterChip[]No[]Selected filter chips shown in the panel
defaultSearchValuestringNo-Initial value used by SearchInput
onSearch(data: T) => voidNo-Called on form submit; T defaults to { search: string }
onSearchChange(event: React.ChangeEvent<HTMLInputElement>) => voidNo-Search field change handler, typically used for controlled search input
onClickRemoveFilter(filter: FilterChip) => voidNo-Called when a chip delete icon is clicked
onClickFilterButton() => voidNo-Called when the filter settings button is clicked

Content Props​

PropTypeRequiredDefaultDescription
searchPlaceholderstringNoフリーワードで検索Placeholder shown in the search input
noFilterTextstringNo条件未設定Text shown when no filters are selected
filterLabelstringNo絞込み設定Label shown on the filter settings button

Layout and Composition Props​

PropTypeRequiredDefaultDescription
childrenBlocksOverride | ReactNodeNo-Override default blocks or render compound children
classNamestringNo-Class name on the root form
sxSxPropsNo-MUI system styles for the root form
spacingStackProps['spacing']No{ xs: 1.5, sm: 2.5 }Space between root blocks
directionStackProps['direction']No'column'Flex direction of the root stack

FilterSearchPanel inherits StackProps (except children, component, and onSubmit) and renders as a form. Submit is handled internally and calls onSearch when provided.

Sub-component props​

Sub-components share context with the root, so they use context values by default and can be overridden locally:

Sub-componentMain propsInherits
FilterSearchPanel.SearchInputsearchPlaceholder, defaultSearchValue, onSearchChange, name, typeTextField
FilterSearchPanel.FilterButtonfilterLabel, onClickFilterButton, childrenButton
FilterSearchPanel.SelectedFilterListfilters, noFilterText, onClickRemoveFilter, childrenBox
FilterSearchPanel.FilterBadgefilter (required), onClickRemoveFilter, childrenChip
FilterSearchPanel.ActionGroupchildren (default: filter button + chip list)Stack

Default UI Blocks​

BlockBuilt onNotes
FilterSearchPanel (root)Stack rendered as formHandles submit internally and provides search/filter context to child blocks
FilterSearchPanel.SearchInputTextField + submit IconButtonname="search", size="small", search icon submits the form
FilterSearchPanel.ActionGroupStackHorizontal row containing the filter button and selected chips; scrollable on overflow
FilterSearchPanel.FilterButtonButton + Tune iconOpens the filter UI through onClickFilterButton; default label is 絞込み設定
FilterSearchPanel.SelectedFilterListBox + TypographyShows noFilterText when empty, otherwise groups chips by groupName
FilterSearchPanel.FilterBadgeChipA removable chip that calls onClickRemoveFilter(filter)

Default render order: searchInput → actionGroup.

Extra field primitives​

PrimitiveBuilt onNotes
FilterSearchPanel.FilterButtonButton + Tune iconOpens your filter UI via onClickFilterButton; default label is 絞込み設定
FilterSearchPanel.SelectedFilterListBox + TypographyShows noFilterText (条件未設定) when empty, otherwise groups chips by groupName
FilterSearchPanel.FilterBadgeChipRemovable chip; delete calls onClickRemoveFilter(filter)

TypeScript​

import * as React from 'react';
import {FilterSearchPanel} from '@nodeblocks/frontend-filter-search-panel-block';

type FilterChip = {
label: string;
key: string;
groupName?: string;
};

type SearchFormData = {
search: string;
};

const filterCatalog: FilterChip[] = [
{label: 'Remote', key: 'work-remote', groupName: 'Work type'},
{label: 'Full-time', key: 'employment-full-time', groupName: 'Employment'},
];

export function ServiceSearchToolbar() {
const [filters, setFilters] = React.useState<FilterChip[]>([
{label: 'Active', key: 'status-active', groupName: 'Status'},
]);

const handleRemoveFilter = (filter: FilterChip) => {
setFilters(current => current.filter(f => f.key !== filter.key));
};

const handleFilterButton = () => {
const nextFilter = filterCatalog.find(option => !filters.some(f => f.key === option.key));

if (nextFilter) {
setFilters(current => [...current, nextFilter]);
}
};

const handleSearch = (data: SearchFormData) => {
const search = data.search.trim();
if (!search) {
return;
}

setFilters(current => [...current, {label: search, key: `search-${Date.now()}`, groupName: 'Search'}]);
};

return (
<FilterSearchPanel<SearchFormData>
filters={filters}
searchPlaceholder="Search services..."
noFilterText="No filters applied"
filterLabel="Filter Settings"
onClickFilterButton={handleFilterButton}
onClickRemoveFilter={handleRemoveFilter}
onSearch={handleSearch}
/>
);
}