FilterMenu

FilterMenu allows a user to choose from a list of options, which can be filtered. It is a composable component which consists of a trigger (for toggling a popover), a search input (for filtering results), a listbox (to display options), and a popover (which contains the listbox when opened). In small screens (tablet and mobile), the Popover will be displayed as a Sheet.

Quick Start

Installation
npm install @adaptavant/eds-core
Import
import { FilterMenu } from '@adaptavant/eds-core';

Default

Basic example

, Select Country
const initialOptions = [
  { code: "61", id: "AUS", name: "Australia" },
  { code: "64", id: "NZL", name: "New Zealand" },
  { code: "91", id: "IND", name: "India" },
  { code: "48", id: "POL", name: "Poland" },
  { code: "44", id: "GBR", name: "Scotland (UK)" },
  { code: "1", id: "USA", name: "United States" },
];

const [selectedOption, setSelectedOption] = React.useState();

const [searchTerm, setSearchTerm] = React.useState('');

function onClear(){
  return setSearchTerm('');
}

function handleInputOnChange(event) {
  return setSearchTerm(event.target.value);
}

const filteredOptions = searchTerm === "" ? initialOptions : initialOptions.filter((country) =>
    country.name.toLowerCase().includes(searchTerm.toLowerCase())
);

function OptionLabel({ name, code }) {
  return (
    <Track as="span">
      {name} +{code}
    </Track>
  );
}

function NoResults() {
  return (
    <Text className="text-secondary text-center text-body-12 py-4">
      No matching results
    </Text>
  );
}

return (
  <Field label="Select">
    <FilterMenu>
      <FilterMenuTrigger placeholder="Select Country">
        {selectedOption ? <OptionLabel {...selectedOption} /> : null}
      </FilterMenuTrigger>
      <FilterMenuPopover>
        <FilterMenuSearchField label="Search Items">
          <FilterMenuSearchInput
            onClear={onClear}
            onChange={handleInputOnChange}
            value={searchTerm}
            placeholder="Search..."
          />
        </FilterMenuSearchField>
        <FilterMenuListbox
          noResultsFallback={<NoResults />}
          options={filteredOptions}
        >
          {(option) => (
            <FilterMenuItem
              id={option.id}
              isSelected={selectedOption?.id === option.id}
              onClick={() => {
                setSelectedOption(option);
              }}
            >
              <OptionLabel {...option} />
            </FilterMenuItem>
          )}
        </FilterMenuListbox>
      </FilterMenuPopover>
    </FilterMenu>
  </Field>
);

Currency Picker

Using FilterMenu's compound component API, we can easily build a CurrencyPicker component from scratch.

, Select currency
const countries = [
  { currency: 'US Dollar', currencyCode: 'USD', currencySymbol: '$', id: "ASM", name: "American Samoa" },
  { currency: 'East Caribbean dollar', currencyCode: 'XCD', currencySymbol: '$', id: "AIA", name: "Anguilla" },
  { currency: 'Australian dollar', currencyCode: 'AUD', currencySymbol: '$', id: "AUS", name: "Australia" },
  { currency: 'Central African CFA franc', currencyCode: 'XAF', currencySymbol: 'FCFA', id: "TCD", name: "Chad" },
  { currency: 'Falkland Islands pound', currencyCode: 'FKP', currencySymbol: '£', id: "FLK", name: "Falkland Islands (Malvinas)"},
  { currency: 'British pound', currencyCode: 'GBP', currencySymbol: '£', id: "GGY", name: "Guernsey"},
  { currency: 'British pound', currencyCode: 'GBP', currencySymbol: '£', id: "IMN", name: "Isle of Man"},
  { currency: 'Indian rupee', currencyCode: 'INR', currencySymbol: '', id: "IND", name: "India"},
  { currency: 'Lebanese pound', currencyCode: 'LBP', currencySymbol: '£', id: "LBN", name: "Lebanon"},
  { currency: 'New Zealand dollar', currencyCode: 'NZD', currencySymbol: '$', id: "NZL", name: "New Zealand"},
  { currency: 'United States dollar', currencyCode: 'USD', currencySymbol: '$', id: "TLS", name: "Timor-Leste"},
  { currency: 'Singapore dollar', currencyCode: 'SGD', currencySymbol: '$', id: "SGP", name: "Singapore"},
  { currency: 'United Arab Emirates dirham', currencyCode: 'AED', currencySymbol: 'إ.د', id: 'ARE', name: 'United Arab Emirates',},
  { currency: 'British pound', currencyCode: 'GBP', currencySymbol: '£', id: 'GBR', name: 'United Kingdom',},
  { currency: 'United States dollar', currencyCode: 'USD', currencySymbol: '$', id: 'USA', name: 'United States',},
];

const [selectedOption, setSelectedOption] = React.useState();

const [searchTerm, setSearchTerm] = React.useState('');

function onClear() {
  return setSearchTerm('');
}

function handleInputOnChange(event) {
  return setSearchTerm(event.target.value);
}

const filteredOptions = searchTerm === "" ? countries : countries.filter((country) => 
  Object.values(country).map((value) => 
    value.toLowerCase()
  ).join(" ").includes(searchTerm.toLowerCase())
);

function escapeRegExp(string) {
	return string.replace(/[+$]/g, '\\$&');
}

function highlightMatchedContent(text, searchTerm) {
	if (!searchTerm) return text;
	const escapedSearchTerm = escapeRegExp(searchTerm);
	const regex = new RegExp(`(${escapedSearchTerm})`, 'gi');
	const parts = text.trim().split(regex);
	return parts.map((part, index) =>
		regex.test(part) ? (
			<span className="font-stronger" key={index}>
				{part}
			</span>
		) : (
			part
		)
	);
}

function OptionLabel({ name, currencyCode, currencySymbol }) {
  return (
    <Track
      className="gap-1"
      classNames={{
        center: 'grow-0',
      }}
      railEnd={<>- {highlightMatchedContent(currencyCode, searchTerm)} {highlightMatchedContent(currencySymbol, searchTerm)}</>}
    >
      <Track classNames={{ center: 'line-clamp-1'}}>
        {highlightMatchedContent(name, searchTerm)}
      </Track>
    </Track>
  );
}

function NoResults() {
  return (
    <Text className="text-secondary text-center text-body-12 py-4">
      No matching results
    </Text>
  );
}

return (
  <Field label="Currency Picker" className="w-60">
    <FilterMenu popoverMatchReferenceWidth>
      <FilterMenuTrigger placeholder="Select currency">
        {selectedOption ? <OptionLabel {...selectedOption} /> : null}
      </FilterMenuTrigger>
      <FilterMenuPopover>
        <FilterMenuSearchField label="Search Items">
          <FilterMenuSearchInput
            onClear={onClear}
            onChange={handleInputOnChange}
            value={searchTerm}
            placeholder="Search..."
          />
        </FilterMenuSearchField>
        <FilterMenuListbox
          noResultsFallback={<NoResults />}
          options={filteredOptions}
        >
          {(option) => (
            <FilterMenuItem
              id={option.id}
              isSelected={selectedOption?.id === option.id}
              onClick={() => {
                setSelectedOption(option);
              }}
            >
              <OptionLabel {...option} />
            </FilterMenuItem>
          )}
        </FilterMenuListbox>
      </FilterMenuPopover>
    </FilterMenu>
  </Field>
);

Country Code Picker

To build a CountryCodePicker component from scratch, follow the code example below.

The following snippet uses useFilteredOptions, a custom hook that manages the search function and returns a filteredOptions array. It also utilises useDeferredValue to improve performance of expensive state updates and spreads some common functions like onClear and onChange to the FilterMenuSearchInput inside a popover via getSearchInputProps.

If you prefer to use your own custom state management, refer to the CurrencyPicker example provided above.

, Select Country
const countries = [
  { currency: 'US Dollar', currencyCode: 'USD', currencySymbol: '$', id: "ASM", name: "American Samoa", dailCode: '1684' },
  { currency: 'East Caribbean dollar', currencyCode: 'XCD', currencySymbol: '$', id: "AIA", name: "Anguilla", dailCode: '1264'},
  { currency: 'Australian dollar', currencyCode: 'AUD', currencySymbol: '$', id: "AUS", name: "Australia", dailCode: '61'},
  { currency: 'Canadian dollar', currencyCode: 'CAD', currencySymbol: '$', id: "CAN", name: "Canada", dailCode: '1'},
  { currency: 'Central African CFA franc', currencyCode: 'XAF', currencySymbol: 'FCFA', id: "TCD", name: "Chad", dailCode: '235'},
  { currency: 'Falkland Islands pound', currencyCode: 'FKP', currencySymbol: '£', id: "FLK", name: "Falkland Islands (Malvinas)", dailCode: '500'},
  { currency: 'British pound', currencyCode: 'GBP', currencySymbol: '£', id: "GGY", name: "Guernsey", dailCode: '44'},
  { currency: 'British pound', currencyCode: 'GBP', currencySymbol: '£', id: "IMN", name: "Isle of Man", dailCode: '44'},
  { currency: 'Indian rupee', currencyCode: 'INR', currencySymbol: '', id: "IND", name: "India", dailCode: '91'},
  { currency: 'Lebanese pound', currencyCode: 'LBP', currencySymbol: '£', id: "LBN", name: "Lebanon", dailCode: '961'},
  { currency: 'New Zealand dollar', currencyCode: 'NZD', currencySymbol: '$', id: "NZL", name: "New Zealand", dailCode: '64'},
  { currency: 'United States dollar', currencyCode: 'USD', currencySymbol: '$', id: "TLS", name: "Timor-Leste", dailCode: '670'},
  { currency: 'Singapore dollar', currencyCode: 'SGD', currencySymbol: '$', id: "SGP", name: "Singapore", dailCode: '65'},
  { currency: 'United Arab Emirates dirham', currencyCode: 'AED', currencySymbol: 'إ.د', id: 'ARE', name: 'United Arab Emirates', dailCode: '971'},
  { currency: 'British pound', currencyCode: 'GBP', currencySymbol: '£', id: 'GBR', name: 'United Kingdom', dailCode: '44'},
  { currency: 'United States dollar', currencyCode: 'USD', currencySymbol: '$', id: 'USA', name: 'United States', dailCode: '1'},
];

const [selectedOption, setSelectedOption] = React.useState();

const { filteredOptions, getSearchInputProps, searchTerm } = useFilteredOptions({
  initialOptions: countries,
  searchFunction: ({ options, searchTerm }) => {
    if (searchTerm === "") return options;
    return options.filter((option) => {
      const getOnlySearchableKeys = Object.fromEntries(Object.entries(option).filter(([key, index]) => 
                                      key !== 'currencyCode' && key !== 'currency')
                                    );
      return Object.values(getOnlySearchableKeys).map((value) => 
        value.toLowerCase()
      ).join(" ").includes(searchTerm.toLowerCase());
    })
  },
});

React.useLayoutEffect(() => {
  const usersCountry = countries.find((country) => country.dailCode === '91');
  if (usersCountry) {
    setSelectedOption(usersCountry);
  }
}, []);

function NoResults() {
  return (
    <Text className="text-secondary text-center text-body-12 py-4">No matching results</Text>
  );
}

function escapeRegExp(string) {
	return string.replace(/[+$]/g, '\\$&');
}

function highlightMatchedContent(text, searchTerm) {
	if (!searchTerm) return text;
	const escapedSearchTerm = escapeRegExp(searchTerm);
	const regex = new RegExp(`(${escapedSearchTerm})`, 'gi');
	const parts = text.trim().split(regex);
	return parts.map((part, index) =>
		regex.test(part) ? (
			<span className="font-stronger" key={index}>
				{part}
			</span>
		) : (
			part
		)
	);
}

return (
  <Field label="Select Country Code" classNames={{ label: 'whitespace-nowrap'}} className="w-20">
    <FilterMenu popoverMaxWidth={280} popoverMaxHeight={300}>
      <FilterMenuTrigger placeholder="Select Country"
        {...(selectedOption
					? {
							children: `+${selectedOption.dailCode}`,
					  }
					: null)
        }
      />
      <FilterMenuPopover>
        <FilterMenuSearchField label="Search Items">
          <FilterMenuSearchInput
            {...getSearchInputProps()}
            placeholder="Search..."
          />
        </FilterMenuSearchField>
        <FilterMenuListbox
          noResultsFallback={<NoResults />}
          options={filteredOptions}
        >
          {(option) => (
            <FilterMenuItem
              id={option.id}
              isSelected={selectedOption.id === option.id}
              onClick={() => {
                setSelectedOption(option);
              }}
            >
              <Track
                className="gap-1"
                classNames={{
                  center: 'grow-0 line-clamp-1',
                }}
                railEnd={<>+{highlightMatchedContent(option.dailCode, searchTerm)}</>}
              >
                {highlightMatchedContent(option.name, searchTerm)}
              </Track>
            </FilterMenuItem>
          )}
        </FilterMenuListbox>
      </FilterMenuPopover>
    </FilterMenu>
  </Field>
);
  • To get all the currency and country data needed to build this component, check out Business utilities package.
  • The popoverMaxWidth, popoverMaxHeight, popoverPlacement, and popoverMatchReferenceWidth props have the same effect on the FilterMenu component as the Dropdown Menu.
  • To control the size of the Trigger, use the width class on the <Field> component.
  • To control the Trigger appearance to "subtle," wrap <FilterMenu> with Inline Field.

Strategy

Use the strategy prop to change the way how popover element will be positioned. By default, the strategy is set to absolute, changes it to fixed when the FilterMenuTrigger is inside a sticky or fixed element.

This option leverages the floating-ui library, which powers the FilterMenuPopover functionality.

const initialOptions = [
  { code: "61", id: "AUS", name: "Australia" },
  { code: "64", id: "NZL", name: "New Zealand" },
  { code: "91", id: "IND", name: "India" },
  { code: "48", id: "POL", name: "Poland" },
  { code: "44", id: "GBR", name: "Scotland (UK)" },
  { code: "1", id: "USA", name: "United States" },
];

const [selectedOption, setSelectedOption] = React.useState(
  initialOptions[0]
);

const [showFixedElement, setShowFixedElement] = React.useState(false);

const onButtonClick = () => {
	setShowFixedElement((prevState) => !prevState)
}

const [searchTerm, setSearchTerm] = React.useState('');

function onClear(){
  return setSearchTerm('');
}

function handleInputOnChange(event) {
  return setSearchTerm(event.target.value);
}

const filteredOptions = searchTerm === "" ? initialOptions : initialOptions.filter((country) =>
    country.name.toLowerCase().includes(searchTerm.toLowerCase())
);

function OptionLabel({ name, code }) {
  return (
    <Track as="span">
      {name} +{code}
    </Track>
  );
}

function NoResults() {
  return (
    <Text className="text-secondary text-center text-body-12 py-4">
      No matching results
    </Text>
  );
}

return (
	<Stack className="w-full gap-4">
		{showFixedElement ? (
			<div 
        className="
          animate-[snackbar-transition_0.3s_cubic-bezier(0.16,_1,_0.3,_1)]
          bg-neutral-secondary
          fixed
          flex
          items-center
          justify-between
          mx-2
          p-4
          right-0
          rounded-8px
          shadow-40
          sm:right-8
          sm:w-[360px]
          top-8
          w-[calc(100%-16px)]
          z-10
        "
      >
        <Field label="Select Items">
          <FilterMenu strategy="fixed">
            <FilterMenuTrigger placeholder="Select Country">
              {selectedOption ? <OptionLabel {...selectedOption} /> : null}
            </FilterMenuTrigger>
            <FilterMenuPopover>
              <FilterMenuSearchField label="Search Items">
                <FilterMenuSearchInput
                  onClear={onClear}
                  onChange={handleInputOnChange}
                  value={searchTerm}
                  placeholder="Search..."
                />
              </FilterMenuSearchField>
              <FilterMenuListbox
                noResultsFallback={<NoResults />}
                options={filteredOptions}
              >
                {(option) => (
                  <FilterMenuItem
                    id={option.id}
                    isSelected={selectedOption.id === option.id}
                    onClick={() => {
                      setSelectedOption(option);
                    }}
                  >
                    <OptionLabel {...option} />
                  </FilterMenuItem>
                )}
              </FilterMenuListbox>
            </FilterMenuPopover>
          </FilterMenu>
        </Field>
        <button
          className="
            focus-visible:focus-ring
            font-stronger
            px-1
            py-0.5
            rounded-4px
            text-body-12
            text-primary
            underline
            underline-offset-2
          "
          onClick={onButtonClick}
        >
          Close
        </button>
			</div>
		) : null}
		<Button onClick={onButtonClick}>
			Show fixed element
		</Button>
	</Stack>
);

Disabled

Utilize the isDisabled prop in the <Field/> to show that a entire FilterMenu isn't usable.

, Australia +61
const initialOptions = [
  { code: "61", id: "AUS", name: "Australia" },
  { code: "64", id: "NZL", name: "New Zealand" },
  { code: "91", id: "IND", name: "India" },
  { code: "48", id: "POL", name: "Poland" },
  { code: "44", id: "GBR", name: "Scotland (UK)" },
  { code: "1", id: "USA", name: "United States" },
];

const [selectedOption, setSelectedOption] = React.useState(
  initialOptions[0]
);

const [searchTerm, setSearchTerm] = React.useState('');

function onClear(){
  return setSearchTerm('');
}

function handleInputOnChange(event) {
  return setSearchTerm(event.target.value);
}

const filteredOptions = searchTerm === "" ? initialOptions : initialOptions.filter((country) =>
    country.name.toLowerCase().includes(searchTerm.toLowerCase())
);

function OptionLabel({ name, code }) {
  return (
    <Track as="span">
      {name} +{code}
    </Track>
  );
}

function NoResults() {
  return (
    <Text className="text-secondary text-center text-body-12 py-4">
      No matching results
    </Text>
  );
}

return (
  <Field label="Select Items" isDisabled>
    <FilterMenu>
      <FilterMenuTrigger placeholder="Select country">
        {selectedOption ? <OptionLabel {...selectedOption} /> : null}
      </FilterMenuTrigger>
      <FilterMenuPopover>
        <FilterMenuSearchField label="Search Items">
          <FilterMenuSearchInput
            onClear={onClear}
            onChange={handleInputOnChange}
            value={searchTerm}
            placeholder="Search..."
          />
        </FilterMenuSearchField>
        <FilterMenuListbox
          noResultsFallback={<NoResults />}
          options={filteredOptions}
        >
          {(option) => (
            <FilterMenuItem
              id={option.id}
              isSelected={selectedOption.id === option.id}
              onClick={() => {
                setSelectedOption(option);
              }}
            >
              <OptionLabel {...option} />
            </FilterMenuItem>
          )}
        </FilterMenuListbox>
      </FilterMenuPopover>
    </FilterMenu>
  </Field>
);

Disabled MenuItem

Utilize the isDisabled prop in the <FilterMenuItem /> component to indicate that a specific item is not selectable.

This enhances user experience by clearly displaying the disabled state. Additionally, for improved accessibility, keyboard navigation will bypass disabled MenuItems in the Listbox.

, Australia +61
const initialOptions = [
  { code: "61", id: "AUS", name: "Australia", disabled: true},
  { code: "64", id: "NZL", name: "New Zealand", disabled: false},
  { code: "91", id: "IND", name: "India", disabled: false},
  { code: "48", id: "POL", name: "Poland", disabled: true},
  { code: "44", id: "GBR", name: "Scotland (UK)", disabled: false},
  { code: "1", id: "USA", name: "United States", disabled: true},
];

const [selectedOption, setSelectedOption] = React.useState(
  initialOptions[0]
);

const [searchTerm, setSearchTerm] = React.useState('');

function onClear(){
  return setSearchTerm('');
}

function handleInputOnChange(event) {
  return setSearchTerm(event.target.value);
}

const filteredOptions = searchTerm === "" ? initialOptions : initialOptions.filter((country) =>
    country.name.toLowerCase().includes(searchTerm.toLowerCase())
);

function OptionLabel({ name, code }) {
  return (
    <Track as="span">
      {name} +{code}
    </Track>
  );
}

function NoResults() {
  return (
    <Text className="text-secondary text-center text-body-12 py-4">
      No matching results
    </Text>
  );
}

return (
  <Field label="Select Items">
    <FilterMenu>
      <FilterMenuTrigger>
        <OptionLabel {...selectedOption} />
      </FilterMenuTrigger>
      <FilterMenuPopover>
        <FilterMenuSearchField label="Search Items">
          <FilterMenuSearchInput
            onClear={onClear}
            onChange={handleInputOnChange}
            value={searchTerm}
            placeholder="Search..."
          />
        </FilterMenuSearchField>
        <FilterMenuListbox
          noResultsFallback={<NoResults />}
          options={filteredOptions}
        >
          {(option) => (
            <FilterMenuItem
              id={option.id}
              isDisabled={option.disabled}
              isSelected={selectedOption.id === option.id}
              onClick={() => {
                setSelectedOption(option);
              }}
            >
              <OptionLabel {...option} />
            </FilterMenuItem>
          )}
        </FilterMenuListbox>
      </FilterMenuPopover>
    </FilterMenu>
  </Field>
);

Note: Disabled item can already be selected option but having any interactions on it won't be possible.

FullScreen on Mobile

On mobile, enable fullScreenForMobile to render the sheet in full-screen with a default close button.\

, Item 1
const initialOptions = [
  { id: "1", value: "Item 1" },
  { id: "2", value: "Item 2" },
  { id: "3", value: "Item 3" },
];
const [selectedOption, setSelectedOption] = React.useState(initialOptions[0]);

const { filteredOptions, getSearchInputProps } = useFilteredOptions({
  initialOptions,
  searchFunction: ({ options, searchTerm }) => {
    if (searchTerm === "") return options;
    return options.filter((option) =>
      option.value.toLowerCase().includes(searchTerm.toLowerCase())
    );
  },
});

return (
  <Field label="Select Items">
    <FilterMenu fullScreenForMobile>
      <FilterMenuTrigger placeholder="Select">
        {selectedOption.value}
      </FilterMenuTrigger>
      <FilterMenuPopover>
        <FilterMenuSearchField label="Search Items">
          <FilterMenuSearchInput
            {...getSearchInputProps()}
            placeholder="Search..."
          />
        </FilterMenuSearchField>
        <FilterMenuListbox
          noResultsFallback={
            <Text className="text-secondary text-center text-body-12 py-4">
              No matching results
            </Text>
          }
          options={filteredOptions}
        >
          {(option) => (
            <FilterMenuItem
              id={option.id}
              isSelected={selectedOption.id === option.id}
              onClick={() => {
                setSelectedOption(option);
              }}
            >
              {option.value}
            </FilterMenuItem>
          )}
        </FilterMenuListbox>
      </FilterMenuPopover>
    </FilterMenu>
  </Field>
);

API Reference

FilterMenu

PropsTypeDescriptionDefault
children((menuState: { isMenuOpen: boolean; triggerProps: TriggerProps }) => ReactNode) | ReactNodeAccepts either a React node or a render function. The render function provides the menu's state (isMenuOpen) and accessibility/interaction props (triggerProps) for the trigger._
closeButtonPropsForMobile?{ label: string, onClick: () => void, size?: IconButtonProps['size'] }Props for the close button that appears on mobile._
mobileFriendly?booleanIndicates whether the filter menu should be displayed as a sheet on mobile devices.true
popoverMatchReferenceWidth?booleanMatch the width of the popover with the reference element.false
popoverMaxHeight?numberThe max height of the filter menu popover.356
popoverMaxWidth?numberThe max width of the filter menu popover.400
popoverOffset?numberThe offset of the filter menu popover.4
popoverPlacement?'bottom' | 'bottom-start' | 'bottom-end'The placement of the filter menu popover in relation to the trigger.'bottom-start'
strategy?'absolute' | 'fixed'The strategy used to position the floating element.'absolute'
titleForMobile?stringIf titleForMobile is provided then the mobile sheet view will have a header with title rendered_
fullScreenForMobilebooleanEnables fullscreen mode for the mobile select menu, making it cover the entire viewport.false

FilterMenuTrigger

The FilterMenuTrigger component is a customizable trigger for a filter menu, built using the Button component. It inherits some Button props and adds few more additional functionalities.

PropTypeDescriptionDefault
children?ReactNodeThe content to be displayed inside the button._
form?stringSpecifies the form element the button is associated with._
iconEnd?ReactNodeThe icon to display after the button children._
iconStart?ReactNodeThe icon to display before the button children._
isLoading?booleanIf true, the button will display a loading indicator.false
isPressed?booleanIf true, the button will be visually styled as pressed and the aria-pressed attribute will be set accordingly.false
loadingLabel?stringText to read out to assistive technologies when button is loading._
onBlur?functionFunction to call when the button loses focus._
onFocus?functionFunction to invoke when the button receives focus._
onKeyDown?functionFunction to invoke when a key is pressed while the button is focused._
placeholder?stringDisplays a placeholder text when no children is provided._

FilterMenuSearchField

The FilterMenuSearchField internally uses Field component. It inherits most of the Field props and adds few more functionalities.

PropTypeDescriptionDefault
childrenReactNodeThe content to be displayed inside the field._
labelReactNodeLabel for the input._
controlId?stringSpecifies the unique identifier for the form control within the component. See Field API for more details._
counter?stringCounter object containing maxValue and value. "maxValue:" maximum value the counter can reach. "value:" current value of the counter._
description?ReactNodeProvide additional information that will aid user input._
errorMessage?ReactNodeMessage to show when the field is invalid._
isDisabled?booleanWhether the field is disabled.false
isRequired?booleanWhether user input is required on the field.false
labelVisibility?'visible' | 'hidden'A label must always be provided for assistive technology, but you may hide it from sighted users when the intent can be inferred from context.'hidden'
secondaryLabel?ReactNodeAdditional context, typically used to indicate that the field is optional._

FilterMenuSearchInput

The FilterMenuSearchInput component internally uses SearchInput component. It inherits most of the SearchInput props and adds few more functionalities.

PropTypeDescriptionDefault
aria-activedescendant?stringidentifies the currently active element when using ARIA widgets._
aria-autocomplete?'inline' | 'list' | 'both' | 'none'Indicates whether input completion suggestions are provided, and their type._
aria-controls?stringIdentifies the element(s) that the input controls._
aria-expanded?booleanIndicates whether the popover is currently expanded or collapsed._
aria-haspopup?'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | 'boolean'Indicates the availability of a popup related to the input._
autoComplete?stringSpecifies whether autocomplete is enabled for the input._
autoCorrect?'on' | 'off'Specifies whether auto-correction is enabled for the input._
autoFocus?booleanAutomatically focuses the input element when it is rendered._
defaultValue?stringSpecifies the initial value of the input for uncontrolled input._
focusContainerRef?React.Ref<HTMLDivElement>Reference to the wrapper FocusContainer element._
inputMode?'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'Hints to browsers about what kind of virtual keyboard to display._
name?stringName of the input, used for form submissions._
onBlur?functionFunction to be invoked when the input loses focus._
onChange?functionFunction to be invoked when a new item is selected._
onFocus?functionFunction to be invoked when the input is focused._
onKeyDown?functionFunction to be invoked when a key is pressed while the input is focused._
onPaste?functionFunction to be invoked when content is pasted into the input._
pattern?stringRegular expression pattern the input's value must match for validation._
placeholder?stringPlaceholder text displayed when the input is empty._
role?stringARIA role for the input._
spellCheck?booleanSpecifies whether the input value should be checked for spelling errors._
type?'email' | 'password' | 'search' | 'tel' | 'text' | 'url'Type of the input element._
value?stringSpecifies the current value for controlled inputs._

FilterMenuPopover

PropTypeDescriptionDefault
childrenReactNodeContent of the filter menu popover._
shouldUsePortal?booleanDetermines whether the popover should be rendered in a React Portal. If true, the popover will be rendered outside the DOM hierarchy of the parent component.true

FilterMenuListbox

PropTypeDescriptionDefault
noResultsFallback?ReactNodeComponent to render when there are no options left in the filtered result._
options?Array of Objects.Options to be rendered in the popover. Note: Each item in the object should have unique id of key property for better caching of children_

FilterMenuItem

PropsTypeDescriptionDefault
childrenReactNodeThe content of the filter menu item._
id?stringAn optional ID for the menu item. If not provided, an ID will be automatically generated._
isDisabled?booleanIndicates if the menu item is currently disabled. Used for styling and accessibility. Applies an aria-disabled attribute.false
isHighlighted?booleanIndicates if the menu item is currently highlighted. Used for styling. Applies a data-highlighted attribute. Note: Controlled internally to support keyboard navigations.false
isSelected?booleanIndicates if the menu item is currently selected. Used for styling. Applies an aria-selected=true and data-selected=true otherwise aria-selected=falsefalse
onClick?() => boolean | voidFunction to be invoked when the item is clicked._
railEnd?ReactNodeThe content/components to appear in the item end._
railStart?ReactNodeThe content/components to appear in the item start._
showSelectionIndicator?booleanControls whether to show the selection indicator (check icon) for selected items.true
size?'standard' | 'large'The size of the filter menu item.'standard'
verticalAlign?'bottom' | 'middle' | 'top'Determines how the rails and center are vertically aligned to each other. A typography or heading can be provided to center align icons and with text that may wrap.'middle'

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.