feat(ui): rename filter and search state handlers to onChange

Standardize the naming convention for state change handlers in
FilterState and SearchState interfaces:

- FilterState: `onFilterChange` → `onChange`
- SearchState: `onSearchChange` → `onChange`

This aligns with React conventions and makes spreading props
directly onto form components cleaner (e.g., `{...search}`).

Also adds a runtime check to ensure useTable mode remains stable
for the component lifetime.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2026-01-08 12:14:10 +01:00
parent 243e5e7139
commit ca7e0e9d4d
4 changed files with 22 additions and 17 deletions
+2 -2
View File
@@ -679,7 +679,7 @@ export interface FieldLabelProps
// @public (undocumented)
export interface FilterState<TFilter> {
// (undocumented)
onFilterChange: (filter: TFilter) => void;
onChange: (value: TFilter) => void;
// (undocumented)
value: TFilter | undefined;
}
@@ -1258,7 +1258,7 @@ export interface SearchFieldProps
// @public (undocumented)
export interface SearchState {
// (undocumented)
onSearchChange: (value: string) => void;
onChange: (value: string) => void;
// (undocumented)
value: string;
}
@@ -20,13 +20,13 @@ import type { SortDescriptor, TableItem, TableProps } from '../types';
/** @public */
export interface FilterState<TFilter> {
value: TFilter | undefined;
onFilterChange: (filter: TFilter) => void;
onChange: (value: TFilter) => void;
}
/** @public */
export interface SearchState {
value: string;
onSearchChange: (value: string) => void;
onChange: (value: string) => void;
}
/** @public */
@@ -98,9 +98,16 @@ export function useTable<T extends TableItem, TFilter = unknown>(
): UseTableResult<T, TFilter> {
const query = useQueryState<TFilter>(options);
const initialModeRef = useRef(options.mode);
if (initialModeRef.current !== options.mode) {
throw new Error(
`useTable mode cannot change from '${initialModeRef.current}' to '${options.mode}'. ` +
`The mode must remain stable for the lifetime of the component.`,
);
}
let pagination: PaginationResult<T> & { reload: () => void };
// Conditional hooks - mode is stable for lifetime of component
if (options.mode === 'complete') {
pagination = useCompletePagination(options, query);
} else if (options.mode === 'offset') {
@@ -125,7 +132,7 @@ export function useTable<T extends TableItem, TFilter = unknown>(
return {
tableProps,
reload: pagination.reload,
filter: { value: query.filter, onFilterChange: query.setFilter },
search: { value: query.search, onSearchChange: query.setSearch },
filter: { value: query.filter, onChange: query.setFilter },
search: { value: query.search, onChange: query.setSearch },
};
}
@@ -30,12 +30,12 @@ import {
type ColumnConfig,
} from '..';
import { Button } from '../../Button';
import { TextField } from '../../TextField';
import { Select } from '../../Select';
import { Flex } from '../../Flex';
import { data as data1 } from './mocked-data1';
import { data as data4 } from './mocked-data4';
import { selectionData, selectionColumns, tableStoriesMeta } from './utils';
import { SearchField } from '../../SearchField';
const meta = {
title: 'Backstage UI/Table/dev',
@@ -186,12 +186,11 @@ export const Search: Story = {
return (
<div>
<TextField
<SearchField
aria-label="Search"
placeholder="Search..."
value={search.value}
onChange={value => search.onSearchChange(value)}
style={{ marginBottom: '16px' }}
{...search}
/>
<Table
columnConfig={columns}
@@ -924,20 +923,19 @@ export const ComprehensiveServerSide: Story = {
return (
<Flex direction="column" gap="4">
<Flex gap="4" align="end">
<TextField
<SearchField
aria-label="Search"
label="Search"
placeholder="Search by name, owner, or description..."
value={search.value}
onChange={search.onSearchChange}
style={{ width: 300 }}
{...search}
/>
<Select
label="Type"
options={typeOptions}
selectedKey={filter.value?.type ?? ''}
onSelectionChange={key =>
filter.onFilterChange({ type: key === '' ? null : String(key) })
value={filter.value?.type ?? ''}
onChange={key =>
filter.onChange({ type: key === '' ? null : String(key) })
}
style={{ width: 180 }}
/>