Skip to main content

Filter ButtonV2

Filter Button is a component with an inline label and highlighted value. It lets users choose one or more items from an options menu.

Use a Filter Button to offer a list of choices where multiple selection is possible.

Example

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
    />
  );
}
Result
Loading...

Variants

Anchor Size

The anchor is available in two sizes: small and medium can be accessed using size prop.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <div style={{ display: 'flex', gap: Spacing10 }}>
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        size="small"
      />
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        size="medium"
      />
    </div>
  );
}
Result
Loading...

Custom Controls

You have Search input and SelectAll checkbox shown by default, which can be hidden using showSearch and showSelectAll props.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      showSearch={false}
      showSelectAll={false}
    />
  );
}
Result
Loading...

Custom List Component

You can use a customList property to define custom component for filter items.

info

Utilize this property to customize the component of the specific filter items. Please keep in mind that you will be responsible for managing styles and keyboard navigation if you opt to pass a custom component. This functionality is most effectively employed in conjunction with the FilterButtonV2Context context.

Live Editor
function MyFilterButtonV2() {
  const CustomListComponent = ({
    label,
    value,
    checked,
    onClick,
    onKeyDown
  }) => {
    const context = useContext(FilterButtonV2Context);
    // Get all values from context
    const {
      setSelectedItems,
      selectedItems,
      setSearchValue,
      setAreCheckedItemsChanged
    } = context;

    console.log({ selectedItems });

    return (
      <div style={{ paddingLeft: '10px' }}>
        <Switch
          checked={checked}
          size="small"
          label={label}
          labelPosition="right"
          onChange={onClick}
          onKeyDown={onKeyDown}
        />
      </div>
    );
  };

  const apps = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const [initialValues, setInitialValues] = useState(initialSelectedItems);

  return (
    <FilterButtonV2
      name="Fruits"
      items={apps}
      selectedItems={initialValues}
      onApply={setInitialValues}
      customList={CustomListComponent}
      itemSize={48}
    />
  );
}
Result
Loading...

Custom Sorting

You can use sortPredicate property to have custom sorting on the results when typing your query. In this example when searching for 'us' or 'ru', unlike the default sorting, 'United States (US)' or 'Russia (RU)' will appear first, respectively.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'Cyprus (CY)',
      value: 'cyprus'
    },
    {
      label: 'United States (US)',
      value: 'united_states'
    },
    {
      label: 'United Kingdom (UK)',
      value: 'united_kingdom'
    },
    {
      label: 'Belgium (BE)',
      value: 'belgium'
    },
    {
      label: 'Germany (DE)',
      value: 'germany'
    },
    {
      label: 'Russia (RU)',
      value: 'russia'
    },
    {
      label: 'Belarus (BY)',
      value: 'belarus'
    },
    {
      label: 'Italy (IT)',
      value: 'italy'
    },
    {
      label: 'China (CN)',
      value: 'china'
    },
    {
      label: 'France (FR)',
      value: 'france'
    }
  ];

  const [selectedItems, setSelectedItems] = useState([]);

  return (
    <FilterButtonV2
      showResetButton
      name="Countries"
      items={items}
      sortPredicate={(a, b, searchValue) => {
        const abbrRegex = new RegExp(`\\b${searchValue}\\b`, 'gi');
        return (
          Number(abbrRegex.test(b.label)) - Number(abbrRegex.test(a.label))
        );
      }}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
    />
  );
}
Result
Loading...

Default Items

You can provide default items which will be used to populate filter once user clicks reset. If not specified filter with multiple selection will reset all items and with single selction filter will reset to initially selected item

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'kiwi',
      value: 'kiwi'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'dragon fruit',
      value: 'dragon fruit'
    },
    {
      label: 'plum',
      value: 'plum'
    },
    {
      label: 'tangerine',
      value: 'tangerine'
    },
    {
      label: 'durian',
      value: 'durian'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'mango',
      value: 'mango'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const defaultItems = [
    {
      label: 'mango',
      value: 'mango'
    }
  ];

  const [multipleSelectedItems, setMultipleSelectedItems] =
    useState(initialSelectedItems);
  const [singleSelectedItems, setSingleSelectedItems] =
    useState(initialSelectedItems);

  return (
    <div style={{ display: 'flex', gap: Spacing10 }}>
      <FilterButtonV2
        showResetButton
        name="Multiple fruits"
        items={items}
        defaultItems={defaultItems}
        selectedItems={multipleSelectedItems}
        onApply={setMultipleSelectedItems}
      />
      <FilterButtonV2
        showResetButton
        multiple={false}
        name="Single fruit"
        items={items}
        defaultItems={defaultItems}
        selectedItems={singleSelectedItems}
        onApply={setSingleSelectedItems}
      />
    </div>
  );
}
Result
Loading...

Description

A user can add a description to the option by using the description prop

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple',
      description: 'I am a description for apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'kiwi',
      value: 'kiwi'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'dragon fruit',
      value: 'dragon fruit',
      description: 'I am a description for dragon fruit'
    },
    {
      label: 'plum',
      value: 'plum'
    },
    {
      label: 'tangerine',
      value: 'tangerine'
    },
    {
      label: 'durian',
      value: 'durian'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'mango',
      value: 'mango'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [multipleSelectedItems, setMultipleSelectedItems] =
    useState(initialSelectedItems);
  const [singleSelectedItems, setSingleSelectedItems] =
    useState(initialSelectedItems);

  return (
    <div style={{ display: 'flex', gap: Spacing10 }}>
      <FilterButtonV2
        showResetButton
        name="Multiple fruits"
        items={items}
        selectedItems={multipleSelectedItems}
        onApply={setMultipleSelectedItems}
      />
      <FilterButtonV2
        showResetButton
        multiple={false}
        name="Single fruit"
        items={items}
        selectedItems={singleSelectedItems}
        onApply={setSingleSelectedItems}
      />
    </div>
  );
}
Result
Loading...

Disable Sort when less items

Sorting is disabled by default when there are less than 6 items. This does not apply to search results.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'pineapple',
      value: 'pineapple'
    },
    {
      label: 'cherry',
      value: 'cherry'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <>
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
      />
    </>
  );
}
Result
Loading...

Disabled

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      disabled
    />
  );
}
Result
Loading...

Disabled Item

We can disable the FilterItem by passing disabled prop in the item object.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana',
      disabled: true
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry',
      disabled: true
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'grapes',
      value: 'grapes'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'pineapple',
      value: 'pineapple'
    },
    {
      label: 'cherry',
      value: 'cherry'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
    />
  );
}
Result
Loading...

disableSelectedSort

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'grapes',
      value: 'grapes'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'pineapple',
      value: 'pineapple'
    },
    {
      label: 'cherry',
      value: 'cherry'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <>
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        disableSelectedSort
      />
    </>
  );
}
Result
Loading...

disableSort

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      disableSort
    />
  );
}
Result
Loading...

disableSort - Custom Sorting with disabled default sorting

Here is an example of using custom sorting when defaut sorting is disabled.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'Cyprus (CY)',
      value: 'cyprus'
    },
    {
      label: 'United States (US)',
      value: 'united_states'
    },
    {
      label: 'United Kingdom (UK)',
      value: 'united_kingdom'
    },
    {
      label: 'Belgium (BE)',
      value: 'belgium'
    },
    {
      label: 'Germany (DE)',
      value: 'germany'
    },
    {
      label: 'Russia (RU)',
      value: 'russia'
    },
    {
      label: 'Belarus (BY)',
      value: 'belarus'
    },
    {
      label: 'Italy (IT)',
      value: 'italy'
    },
    {
      label: 'China (CN)',
      value: 'china'
    },
    {
      label: 'France (FR)',
      value: 'france'
    }
  ];

  const [selectedItems, setSelectedItems] = useState([]);

  return (
    <FilterButtonV2
      showResetButton
      name="Countries"
      items={items}
      sortPredicate={(a, b, searchValue) => {
        const abbrRegex = new RegExp(`\\b${searchValue}\\b`, 'gi');
        return (
          Number(abbrRegex.test(b.label)) - Number(abbrRegex.test(a.label))
        );
      }}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      disableSort
    />
  );
}
Result
Loading...

Empty state custom component

You can use a emptyStateComponent property to define the empty state component for the filter instead of the default one. Empty state is shown when there are no items to display (e.g. when we have API error and initial items array is empty):

Live Editor
function MyFilterButtonV2() {
  const items = [];

  const [selectedItems, setSelectedItems] = useState([]);

  const EmptyState = () => (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <Icon name="Info" size="large" color="neutralSecondary" />
      <div>No data to display</div>
    </div>
  );

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      emptyStateComponent={EmptyState}
    />
  );
}
Result
Loading...

Default empty state(uses empty-state-message label to set the message):

Live Editor
function MyFilterButtonV2() {
  const items = [];

  const [selectedItems, setSelectedItems] = useState([]);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
    />
  );
}
Result
Loading...

Enable Zero Selection

To have no filters applied, you can enable the prop enableZeroSelection to true.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      enableZeroSelection
    />
  );
}
Result
Loading...

You can use this prop to display a warning message in the footer. Important note: footerWarningMessage prop and the Reset Button cannot be used at the same time.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <div style={{ display: 'flex', gap: Spacing10 }}>
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        footerWarningMessage="Select a maximum of 6 apps."
      />
    </div>
  );
}
Result
Loading...

Full width trigger

You can use isFullWidthTrigger property to allow the trigger to take the available width. If isFullWidthTrigger is true the menu takes the same width as the trigger.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      isFullWidthTrigger
    />
  );
}
Result
Loading...

Icons, App Icons and Platforms

Live Editor
function MyFilterButtonV2() {
  const items =[
      {
        value: 'amazingmarket',
        label: 'Amazing Market',
        iconName: 'DuoStackedBarChart',
        platform: [
          "PlatformWindowsFilled"
        ]
      },
      {
        value: 'cryptoharvest',
        label: 'Crypto Harvest',
        iconName: 'DuoBarChart',
        platform: [
          'PlatformAndroidFilled',
          'PlatformVizioTVFilled'
        ]
      },
      {
        value: 'piechart',
        label: 'DuoTone PieChart',
        iconName: 'DuoPieChart'
      },
      {
        value: 'leardroid',
        label: 'Learn Droid with loooooong text', 
        iconName: 'DuoBarChart',
        platform: [
          'PlatformAndroidFilled',
          'PlatformVizioTVFilled',
          'PlatformIosFilled',
          'PlatformWindowsFilled'
        ]
      }
    ];

  const [selectedItems, setSelectedItems] = useState(items);

  return (
    <FilterButtonV2
      name="Apps"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
    />
  );
}
Result
Loading...

InfoText

A text value to show information related to the selection.

You can change the position of the info text to be on top of the FilterButtonV2 as well.

warning

Please avoid using the Info Text on top of the FilterButtonV2 when you also need the optional label.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      infoText='Sample info Text'
    />
  );
}
Result
Loading...

We also support text tags inside the Info Text. Like strong, b, i and a.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);
  const infoText = `Info <b>text</b> really <i>long</i> that <a href='#'> goes </a> on to a <strong> second </strong>line`;

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      infoText={infoText}
    />
  );
}
Result
Loading...

Invalid State

A user can set a invalid state when the selection is improper.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      infoText={'I am sample invalid infoText'}
      invalid
    />
  );
}
Result
Loading...

isApplyButtonDisabled

You can use this prop to disable the Apply button.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <div style={{ display: 'flex', gap: Spacing10 }}>
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        isApplyButtonDisabled={true}
      />
    </div>
  );
}
Result
Loading...

isGroupLabel

You can use this prop to add a Group Label.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'Fruits',
      value: 'fruits',
      isGroupLabel: true
    },
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    },
    {
      label: 'Vegetables',
      value: 'vegetables',
      isGroupLabel: true
    },
    {
      label: 'Beans',
      value: 'beans'
    },
    {
      label: 'Potato',
      value: 'potato'
    },
    {
      label: 'Onion',
      value: 'onion'
    },
    {
      label: 'Spinach',
      value: 'spinach'
    },
    {
      label: 'Tomato',
      value: 'tomato'
    },
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'Spinach',
      value: 'spinach'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <div style={{ display: 'flex', gap: Spacing10 }}>
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        disableSelectedSort
      />
    </div>
  );
}
Result
Loading...

Loading More Data

The component supports virtual infinite loading by adding following props:

  • onLoadMoreData a callback function to load more data.
  • hasMoreData
  • isLoadingMoreData
  • loadingMoreDataLabel custom label of status indicator.

To integrate virtual infinite loading with React Query please refer to useInfiniteQuery hook and this example.

Live Editor
function MyFilterButtonV2() {

  const generateMoreData = useCallback(
    (offset = 0, chunkSize = 25) =>
      Array.from({ length: chunkSize }, (_, index) => {
        const id = offset + index + 1;

        return {
          label: `Label ${id}`,
          value: `value_${id}`,
        };
      }),
    []
  );

  const maxNumberOrRows = 100;
  const initialData = useMemo(() => generateMoreData(), [generateMoreData]);
  const [items, setItems] = useState(initialData);
  const [selectedItems, setSelectedItems] = useState(initialData);
  const [isLoadingMoreData, setIsLoadingMoreData] = useState(false);

  const onLoadMoreData = useCallback(() => {
    setIsLoadingMoreData(true);
    setTimeout(() => {
      const generatedData = generateMoreData(items.length);
      setItems((preItems) => [...preItems, ...generatedData]);
      setSelectedItems((selectedItems) => [...selectedItems, ...generatedData]);
      setIsLoadingMoreData(false);
    }, 3000);
  }, [items.length, generateMoreData]);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      hasMoreData={items.length < maxNumberOrRows}
      isLoadingMoreData={isLoadingMoreData}
      onLoadMoreData={onLoadMoreData}
    />
  );
}
Result
Loading...

Loading State

You can use a isLoading property in order to display the loading indicator.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      isLoading
    />
  );
}
Result
Loading...

The menu list is available in three sizes: small, medium and large can be accessed using menuSize prop. It is ignored if isFullWidthTrigger is true. In this case the menu takes the same width as the trigger.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <div style={{ display: 'flex', gap: Spacing10 }}>
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        menuSize="small"
      />
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        menuSize="medium"
      />
      <FilterButtonV2
        name="Fruits"
        items={items}
        selectedItems={selectedItems}
        onApply={setSelectedItems}
        menuSize="large"
      />
    </div>
  );
}
Result
Loading...

Mode Shifter

In FilterButtonV2 we have a mode shifter which can be enabled when modeShifter is true and onModeChange handler is provided. We have 2 mode include and exclude, which can be controlled using onModeChange handler which uses an argument of either of 2 modes.

There are 2 cases where the exclude selection jumps to the it equivalant include state.

Nothing Selected in Exclude = All Selected in Include

All Items Selected in Exclude = Nothing Seleted in Include.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);
  const [modeType, setModeType] = useState('include');
  const onModeChange = (value) => {
    setModeType(value);
  };

  const onApplyToFilter = (selectedItems, mode) => {
    console.log(selectedItems, mode);
    setSelectedItems(selectedItems);
  };

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={onApplyToFilter}
      showResetButton
      modeShifter
      modeType={modeType}
      onModeChange={onModeChange}
    />
  );
}
Result
Loading...

You can also change the Mode labels using the modeLabels property.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(items);
  const [modeType, setModeType] = useState('include');
  const onModeChange = (value) => {
    setModeType(value);
  };

  const modeLabels = {
    include: 'enthalten',
    exclude: 'ausschließen'
  };

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
      modeShifter
      modeType={modeType}
      onModeChange={onModeChange}
      modeLabels={modeLabels}
    />
  );
}
Result
Loading...

You can use the onSearch prop to provide an async search functionality.

Pay attention to these points:

  • debounce onSearch calls if needed
  • cancel/abort previous search requests, while typing/making a new one
  • apply selected to initial items. If initial items does not contain them. So the user can see all selected items.
Live Editor
function FilterButtonV2WithAsyncSearch() {

  const generateMoreData = useCallback(
    (searchString: string = '') =>
      Array.from({ length: 10 }, (_, index) => {
        const id = index + 1;

        return {
          label: `Label ${id} ${searchString}`,
          value: `value_${id}_${searchString}`,
        };
      }),
    []
  );

  const initialData = useMemo(() => generateMoreData(), [generateMoreData]);
  const [items, setItems] = useState(initialData);
  const [selectedItems, setSelectedItems] = useState(initialData.slice(0, 2));
  const [preSelectedItems, setPreSelectedItems] = useState(selectedItems);
  const [isLoading, setIsLoading] = useState(false);
  const loadingTimer = useRef<NodeJS.Timeout | null>(null);

  const updateInitialItems = useCallback((selectedItems: any[]) => {
    const missedSelectedItems = selectedItems.filter(item => {
      return !initialData.some(initialItem => item.value === initialItem.value);
    });
    const initialDataAndSelectedItems = initialData.concat(missedSelectedItems);

    setItems(initialDataAndSelectedItems);
  }, [initialData]);

  const cancelSearchRequest = useCallback(() => {
    loadingTimer.current && clearTimeout(loadingTimer.current);
    setIsLoading(false);
  }, []);

  const onSearch = useCallback((searchString: string) => {
    console.log('onSearch', searchString);
    cancelSearchRequest();

    if (searchString.length > 0) {
      setIsLoading(true);

      loadingTimer.current = setTimeout(() => {
        const generatedData = generateMoreData(searchString);
        setItems(generatedData);

        setIsLoading(false);
      }, 1500);
    } else {
      updateInitialItems(preSelectedItems);

      setIsLoading(false);
    }
  }, [initialData, preSelectedItems, generateMoreData]);

  const onApply = useCallback((appliedItems: any[]) => {
    console.log('onApply', appliedItems);
    setSelectedItems(appliedItems);
    updateInitialItems(appliedItems);
    cancelSearchRequest();
  }, []);

  const onReset = useCallback(() => {
    const initialSelectedItems = initialData.slice(0, 2);
    return initialSelectedItems;
  }, [initialData]);

  const onHide = useCallback(() => {
    console.log('onHide', selectedItems);
    updateInitialItems(selectedItems);
    cancelSearchRequest();
  }, [selectedItems]);

  return (
    <FilterButtonV2
      name="Async Search"
      items={items}
      selectedItems={selectedItems}
      onApply={onApply}
      isLoading={isLoading}
      onSearch={onSearch}
      onHide={onHide}
      showResetButton
      onReset={onReset}
      onSelectionChange={setPreSelectedItems}
    />
  );
}
Result
Loading...

onSelectionChange

The onSelectionChange prop is an onChange callback. You can use this prop to send information about the selected items to the parent component.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);
  const [message, setMessage] = useState('');
  const [isDisabled, setIsDisabled] = useState(false);



  const onSelectionChange = (items) => {
    if(items.length > 6) {
      setMessage('Select a maximum of 6 apps.');
      setIsDisabled(true);
    }
    else {
      setMessage('');
      setIsDisabled(false);
    }
  }
  
  const renderValue = () => {
    return (
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="Pin" css={{ marginRight: Spacing10 }} />
        <span>Pin apps</span>
      </div>
    );
  };

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      onSelectionChange={onSelectionChange}
      footerWarningMessage={message}
      renderValue={renderValue}
      isApplyButtonDisabled={isDisabled}
    />
  );
}
Result
Loading...

Render Items

You can use a renderItem property to define custom render function for filter items.

info

The filter uses the virtualization so if you are about to change the height of items do not forget to specify an itemSize property.

Live Editor
function MyFilterButtonV2() {
  const apps = [
    {
      label: 'apple',
      value: 'apple',
      iconName: 'DuoBarChart',
      platform: [
        'PlatformAndroidFilled',
        'PlatformVizioTVFilled',
        'PlatformIosFilled',
        'PlatformWindowsFilled'
      ],
      description: 'a test description for FilterButtonV2 item',
      name: 'My name is Apple',
      subItems: [
        {
          label: 'banana',
          value: 'banana'
        }
      ]
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  const renderItem = ({
    label,
    value,
    iconName,
    platform,
    description,
    name,
    subItems
  }) => (
    <div>
      <div
        style={{
          display: 'flex',
          alignItems: 'center',
          width: '100%'
        }}
      >
        <div>
          {iconName && <Icon
            size="large"
            name={iconName}
            css={{ marginLeft: Spacing10 }}
          />}
          <span style={{ flex: 1, textAlign: 'left' }}>{label}</span>
          { name && value ? <span>{name + " " +value}</span> :null}
        </div>
        {platform && platform.length !==0 ? platform.map((item, i) => 
          (
            <Icon
              size="small"
              name={item}
              css={{ marginLeft: Spacing10 }}
              key={i}
            />
          ) 
        ) : null}
      </div>
     {description && <span>{description}</span>}
      {subItems && subItems.length !== 0 ?
        subItems.map((sub, i) => (
          <span style={{paddingTop: "5px"}} key={i}>{sub.label + " I am a Sub Item"}</span>
        )) : null
      }
    </div>
  );

  return (
    <FilterButtonV2
      name="Fruits"
      items={apps}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      renderItem={renderItem}
      itemSize={48}
    />
  );
}
Result
Loading...

RenderValue

You can use a renderValue property to define custom render function for the filter value. When specified this function is used to render a custom element within the trigger.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  const renderValue = () => {
    return (
      <div style={{ display: 'flex', alignItems: 'center' }}>
        <Icon name="CogWheel" css={{ marginRight: Spacing10 }} />
        <span>All fruits</span>
        <Badge
          color="negative"
          label="Custom label"
          css={{ marginLeft: Spacing10 }}
        />
      </div>
    );
  };

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      renderValue={renderValue}
    />
  );
}
Result
Loading...

Reset Button

You can use a showResetButton property in order to display the reset button. The reset button selects all option items.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      showResetButton
    />
  );
}
Result
Loading...

Single Value Selection

You can use multiple property to define if you want to have multiple or single selection for defined options.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'kiwi',
      value: 'kiwi'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'dragon fruit',
      value: 'dragon fruit'
    },
    {
      label: 'plum',
      value: 'plum'
    },
    {
      label: 'tangerine',
      value: 'tangerine'
    },
    {
      label: 'durian',
      value: 'durian'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'mango',
      value: 'mango'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  return (
    <FilterButtonV2
      showResetButton
      multiple={false}
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
    />
  );
}
Result
Loading...

Translate

You can use a getElementLabel property to define the text for all elements within the Filter. This function will be called with one of the following values as an argument:

  type FilterButtonV2TextElements =
| 'apply-button'
| 'reset-button'
| 'select-all-default'
| 'select-all-searched'
| 'loading-message'
| 'not-found-message'
| 'empty-state-message'
| 'all-selected-badge-label'
| 'search-input-placeholder'

Each value matches the element within the Filter, e.g. 'apply-button' specifies the label for the Apply button etc.

Live Editor
function MyFilterButtonV2() {
  const items = [
    {
      label: 'apple',
      value: 'apple'
    },
    {
      label: 'banana',
      value: 'banana'
    },
    {
      label: 'orange',
      value: 'orange'
    },
    {
      label: 'cherry',
      value: 'cherry'
    },
    {
      label: 'peach',
      value: 'peach'
    },
    {
      label: 'pear',
      value: 'pear'
    },
    {
      label: 'grapefruit',
      value: 'grapefruit'
    },
    {
      label: 'blackberry',
      value: 'blackberry'
    },
    {
      label: 'strawberry',
      value: 'strawberry'
    },
    {
      label: 'pineapple',
      value: 'pineapple'
    }
  ];

  const initialSelectedItems = [
    {
      label: 'apple',
      value: 'apple'
    }
  ];

  const [selectedItems, setSelectedItems] = useState(initialSelectedItems);

  const getElementLabel = (element) => {
    if (element === 'apply-button') {
      return 'Hello';
    }
    return 'world';
  };

  return (
    <FilterButtonV2
      name="Fruits"
      items={items}
      selectedItems={selectedItems}
      onApply={setSelectedItems}
      getElementLabel={getElementLabel}
    />
  );
}
Result
Loading...

Props

NameTypeDefault
onApply *
(items: T[], modeType?: Mode | undefined) => void
selectedItems *
T[]
items *
T[]
name *
string
multiple
boolean
true
defaultItems
T[]
disabled
boolean
invalid
boolean
isLoading
boolean
size
"small" | "medium"
"small"
menuSize
"small" | "medium" | "large"
"small"
itemSize
number
32
showSearch
boolean
true
showSelectAll
boolean
true
showResetButton
boolean
modeShifter
boolean
enableZeroSelection
boolean
false
modeType
"include" | "exclude"
modeLabels
{ include: string; exclude: string; }
onReset
((initialItems: T[]) => T[])
onHide
(() => void)
onSearch
((searchString: string) => void)
onSelectionChange
((items: T[]) => void)
onModeChange
((modeType?: Mode) => void)
renderItem
((item: T) => ReactNode)
renderValue
((selectedItems: T[]) => ReactNode)
getElementLabel
((element: FilterButtonTextElements) => string)
searchPredicate
SearchItemPredicate<T & FilterItemV2 & FilterItemGroupedV2>
sortPredicate
SortPredicate<T & FilterItemV2 & FilterItemGroupedV2>
id
string
aria-label
string
aria-labelledby
string
zIndex
number
isFullWidthTrigger
boolean
isApplyButtonDisabled
boolean
footerWarningMessage
string
emptyStateComponent
(() => ReactNode)
customList
ComponentType<T>
onLoadMoreData
((startIndex?: number, stopIndex?: number) => void | Promise<void>) | undefined
isLoadingMoreData
boolean
hasMoreData
boolean
loadingMoreDataLabel
string
infoText
string
disableSort
boolean
false
disableSelectedSort
boolean
false
data-{foo}
Data attributes can be used by testing libraries to retrieve components or assert their existence
string
* - the prop is required.