# Filters layout

A composable and responsive layout component for filters

Tabs: [Guidelines](/components/web/filters-layout/filters-layout.guidelines.md) · [Code](/components/web/filters-layout/filters-layout.code.md)

## Basic usage

First, import the components.

```tsx
import {FiltersLayout, SortSelect} from '@pleo-io/telescope'
```

`FiltersLayout` places a search field, a group of filters, a sort select and the filter results in a consistent, responsive layout. Use it with [Search](/components/web/search/search.guidelines), [Filter menu](/components/web/filter-menu/filter-menu.guidelines) or `FilterButton`, and [Filter results](/components/web/filter-results/filter-results.guidelines).

```tsx
const Example = () => {
    const [search, setSearch] = React.useState('')
    const [sorting, setSorting] = React.useState('newest')
    const [hasAppliedFilters, setHasAppliedFilters] = React.useState(false)

    return (
        <FiltersLayout>
            <FiltersLayout.SearchWrapper>
                <Search label="Search expenses" value={search} onValueChange={setSearch} />
            </FiltersLayout.SearchWrapper>
            <FiltersLayout.FiltersWrapper>
                <FiltersLayout.FiltersGroup>
                    <FilterButton
                        hasAppliedFilters={hasAppliedFilters}
                        onClick={() => setHasAppliedFilters((applied) => !applied)}
                    >
                        Category
                    </FilterButton>
                    <FilterButton>Status</FilterButton>
                </FiltersLayout.FiltersGroup>
                <SortSelect
                    options={[
                        {label: 'Newest first', value: 'newest'},
                        {label: 'Oldest first', value: 'oldest'},
                    ]}
                    value={sorting}
                    onChange={setSorting}
                />
            </FiltersLayout.FiltersWrapper>
            <FiltersLayout.FiltersResultsWrapper isVisible={hasAppliedFilters}>
                <FilterResults>
                    <FilterResults.Text>1 filter applied</FilterResults.Text>
                    <FilterResults.Clear onClick={() => setHasAppliedFilters(false)}>
                        Clear filters
                    </FilterResults.Clear>
                </FilterResults>
            </FiltersLayout.FiltersResultsWrapper>
        </FiltersLayout>
    )
}

render(<Example />)
```

## Layout

- `FiltersLayout.SearchWrapper` sizes the search field. It takes at least 320px and grows to fill the row. Pass `$stacked` to give it a full row of its own, or `$withoutFilters` to keep it at 320px when there are no filters next to it.
- `FiltersLayout.FiltersWrapper` holds the filters. When it has a second child, such as a `SortSelect`, that child is pushed to the end of the row.
- `FiltersLayout.FiltersGroup` wraps a group of filters onto several lines on small screens.
- `FiltersLayout.FiltersResultsWrapper` animates the filter results in and out when `isVisible` changes, and respects the user's reduced motion setting.

## Sorting

`SortSelect` is a non-searchable [Select](/components/web/select/select.guidelines) with a sort icon. It is controlled: pass the `options`, the selected `value` and an `onChange` handler, which receives the value of the selected option.

```tsx
const Example = () => {
    const [sorting, setSorting] = React.useState('newest')

    return (
        <SortSelect
            options={[
                {label: 'Newest first', value: 'newest'},
                {label: 'Oldest first', value: 'oldest'},
            ]}
            value={sorting}
            onChange={setSorting}
        />
    )
}

render(<Example />)
```

## Localisation

`SortSelect` is named "Sort by:" for assistive technology. The label comes from the `TelescopeProvider` translations under `FiltersLayout.SortByLabel`.

## Accessibility

- The parts render in reading order, so keyboard users move from the search field through the filters to the sort select.
- `SortSelect` has an accessible name from the localised `FiltersLayout.SortByLabel` translation.
- `FiltersLayout.FiltersResultsWrapper` skips its animation when the user prefers reduced motion.

## API reference

### FiltersLayout

#### `testId`

Type: `string | undefined`

Default: -

No description available

### FiltersResultsWrapper

#### `isVisible` (required)

Type: `boolean`

Default: -

No description available

### SortSelect

#### `disabled`

Type: `boolean`

Default: -

No description available

#### `onChange` (required)

Type: `(value: string) => void`

Default: -

No description available

#### `onOpen`

Type: `(() => void) | undefined`

##### Allowed values

- `() => void`

Default: -

No description available

#### `options` (required)

Type: `Record<"label" | "value", string>[]`

Default: -

No description available

#### `testId`

Type: `string | undefined`

Default: -

No description available

#### `value` (required)

Type: `string`

Default: -

No description available
