Skip to main content

ColumnHeaderFilter

A column header component that combines sorting and filtering UI. Displays the column name, sort indicator, and filter icon. Clicking the filter icon opens a popover with filter controls (text input, multi-select checkboxes, people picker, or date range).

Import​

import { ColumnHeaderFilter } from '@alaarab/ogrid-react-radix';
// or
import { ColumnHeaderFilter } from '@alaarab/ogrid-react-fluent';

Props (React)​

PropTypeDefaultDescription
columnKeystringRequiredUnique column identifier.
columnNamestringRequiredDisplay name shown in the header.
filterTypeColumnFilterTypeRequiredFilter type: 'none', 'text', 'multiSelect', 'people', or 'date'.
isSortedbooleanfalseWhether this column is currently sorted.
isSortedDescendingbooleanfalseSort direction (only relevant when isSorted is true).
onSort() => voidundefinedCallback when user clicks the header to sort.
selectedValuesstring[][]Selected values for multi-select filter.
onFilterChange(values: string[]) => voidundefinedCallback when multi-select filter is applied.
optionsstring[][]Available options for multi-select filter.
isLoadingOptionsbooleanfalseLoading state for multi-select options.
textValuestring''Current text filter value.
onTextChange(value: string) => voidundefinedCallback when text filter is applied.
selectedUserUserLikeundefinedSelected user for people filter.
onUserChange(user: UserLike | undefined) => voidundefinedCallback when people filter is applied or cleared.
peopleSearch(query: string) => Promise<UserLike[]>undefinedAsync search function for people picker.
dateValueIDateFilterValueundefinedCurrent date range filter value.
onDateChange(value: IDateFilterValue | undefined) => voidundefinedCallback when date filter is applied or cleared.

Filter Types​

Text Filter (filterType: 'text')​

Shows a text input. The user types a query and clicks "Apply" to filter rows containing the text (case-insensitive).

Required props: textValue, onTextChange

Multi-Select Filter (filterType: 'multiSelect')​

Shows a searchable list of checkboxes. The user selects one or more values and clicks "Apply" to filter rows matching any selected value.

Required props: selectedValues, onFilterChange, options

Optional props: isLoadingOptions

People Filter (filterType: 'people')​

Shows a people picker with async search. The user types a name or email, selects a person from the results, and clicks "Apply" to filter rows assigned to that person.

Required props: selectedUser, onUserChange, peopleSearch

Date Filter (filterType: 'date')​

Shows a date range picker with "From" and "To" inputs (ISO YYYY-MM-DD format). The user selects a date range and clicks "Apply" to filter rows within that range.

Required props: dateValue, onDateChange

No Filter (filterType: 'none')​

No filter icon is shown. Only the sort indicator is displayed (if the column is sortable).

Usage​

Text Filter (React)​

import { ColumnHeaderFilter } from '@alaarab/ogrid-react-radix';

function MyHeader() {
const [textValue, setTextValue] = useState('');

return (
<ColumnHeaderFilter
columnKey="name"
columnName="Product Name"
filterType="text"
textValue={textValue}
onTextChange={setTextValue}
/>
);
}

Multi-Select Filter (React)​

import { ColumnHeaderFilter } from '@alaarab/ogrid-react-radix';

function MyHeader() {
const [selectedValues, setSelectedValues] = useState<string[]>([]);
const options = ['Active', 'Pending', 'Completed'];

return (
<ColumnHeaderFilter
columnKey="status"
columnName="Status"
filterType="multiSelect"
selectedValues={selectedValues}
onFilterChange={setSelectedValues}
options={options}
/>
);
}

People Filter (React)​

import { ColumnHeaderFilter } from '@alaarab/ogrid-react-radix';
import type { UserLike } from '@alaarab/ogrid-react-radix';

function MyHeader() {
const [selectedUser, setSelectedUser] = useState<UserLike | undefined>();

const searchPeople = async (query: string): Promise<UserLike[]> => {
// Call your user directory API
const response = await fetch(`/api/users/search?q=${query}`);
return response.json();
};

return (
<ColumnHeaderFilter
columnKey="assignee"
columnName="Assigned To"
filterType="people"
selectedUser={selectedUser}
onUserChange={setSelectedUser}
peopleSearch={searchPeople}
/>
);
}

With Sorting (React)​

import { ColumnHeaderFilter } from '@alaarab/ogrid-react-radix';

function MyHeader() {
const [sortBy, setSortBy] = useState('name');
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');

const handleSort = () => {
if (sortBy === 'name') {
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
} else {
setSortBy('name');
setSortDirection('asc');
}
};

return (
<ColumnHeaderFilter
columnKey="name"
columnName="Product Name"
filterType="text"
isSorted={sortBy === 'name'}
isSortedDescending={sortDirection === 'desc'}
onSort={handleSort}
textValue=""
onTextChange={() => {}}
/>
);
}

Accessibility​

ColumnHeaderFilter implements WCAG 2.1 AA standards with full keyboard and screen reader support.

ARIA Attributes​

<th scope="col">
<button aria-label="Sort by Product Name, currently unsorted">
Product Name
</button>
<button aria-label="Filter Product Name" aria-expanded="false" aria-haspopup="dialog">
▼
</button>
</th>
AttributeElementPurpose
scope="col"<th>Associates header with its column for screen readers
aria-label (sort button)<button>Describes current sort state: "Sort by X, currently unsorted/ascending/descending"
aria-label (filter button)<button>Describes filter action: "Filter X"
aria-expandedFilter buttonIndicates popover open/closed state ("true" / "false")
aria-haspopup="dialog"Filter buttonIndicates the button triggers a dialog
role="dialog"Filter popoverIdentifies the popover as a modal dialog
aria-labelFilter popoverProvides context: "Filter Product Name"

Keyboard Navigation​

Header Interaction:

  • Tab - Move focus to sort button, then filter button
  • Enter / Space - Activate focused button (toggle sort or open filter)
  • Shift+Tab - Move focus backwards

Filter Popover (All Types):

  • Escape - Close popover without applying changes
  • Tab / Shift+Tab - Navigate inputs and buttons
  • Enter (on Apply button) - Apply filter and close popover

Text Filter:

  • Type to enter filter text
  • Enter - Apply filter immediately (no button click needed)

Multi-Select Filter:

  • ↑ ↓ - Navigate checkbox list
  • Space - Toggle focused checkbox
  • Ctrl+A - Select all options
  • Type to search options

People Filter:

  • Type to search people
  • ↓ - Move to search results list
  • ↑ ↓ - Navigate search results
  • Enter - Select focused person
  • Backspace - Clear selected person

Date Filter:

  • Tab - Move between "From" and "To" inputs
  • Type date in YYYY-MM-DD format
  • Date picker (if supported): Arrow keys to navigate calendar

Focus Management​

  • Focus trap: Focus is trapped within the filter popover when open
  • Focus restoration: When closing the popover, focus returns to the filter button
  • Focus visible: :focus-visible styles show 2px solid outline (--ogrid-accent) on keyboard navigation

Screen Reader Support​

Screen readers announce:

  • Header navigation: "Product Name column header, sortable"
  • Sort state: "Sorted by Product Name, ascending"
  • Filter button: "Filter Product Name button, collapsed/expanded"
  • Filter applied: "Filter applied: Status equals Active"
  • Filter cleared: "Filter cleared from Status column"

Tested with NVDA, JAWS, VoiceOver, and Narrator.

High Contrast Mode​

  • Filter icons remain visible in high contrast mode
  • Focus indicators use system Highlight color
  • Popover borders use system ButtonText color

See the Accessibility Guide for complete documentation.

Styling​

All UI packages provide default styles matching their design system (Radix UI, Fluent UI). Styles are scoped to avoid conflicts.

CSS custom properties (all packages):

  • --ogrid-header-bg - Header background color
  • --ogrid-border - Border color
  • --ogrid-fg - Foreground text color

See Also​