diff --git a/packages/jsapi-components/src/spectrum/ComboBox.tsx b/packages/jsapi-components/src/spectrum/ComboBox.tsx index 7935495255..8c345a1029 100644 --- a/packages/jsapi-components/src/spectrum/ComboBox.tsx +++ b/packages/jsapi-components/src/spectrum/ComboBox.tsx @@ -3,6 +3,7 @@ import { NormalizedItem, SpectrumComboBoxProps, } from '@deephaven/components'; +import { useCallback } from 'react'; import { PickerWithTableProps } from './PickerProps'; import { usePickerProps } from './utils'; @@ -11,11 +12,25 @@ export type ComboBoxProps = PickerWithTableProps< >; export function ComboBox(props: ComboBoxProps): JSX.Element { - const pickerProps = usePickerProps(props); + const { + onInputChange: onInputChangeInternal, + onSearchTextChange, + ...pickerProps + } = usePickerProps(props); + + const onInputChange = useCallback( + (value: string) => { + onInputChangeInternal?.(value); + onSearchTextChange(value); + }, + [onInputChangeInternal, onSearchTextChange] + ); + return ( ); } diff --git a/packages/jsapi-components/src/spectrum/Picker.tsx b/packages/jsapi-components/src/spectrum/Picker.tsx index 854d4bc2a7..207ee81dee 100644 --- a/packages/jsapi-components/src/spectrum/Picker.tsx +++ b/packages/jsapi-components/src/spectrum/Picker.tsx @@ -3,7 +3,7 @@ import { PickerProps } from './PickerProps'; import { usePickerProps } from './utils'; export function Picker(props: PickerProps): JSX.Element { - const pickerProps = usePickerProps(props); + const pickerProps = usePickerProps(props); return ( Promise; onChange: (key: ItemKey | null) => void; onScroll: (event: Event) => void; + onSearchTextChange: (searchText: string) => void; }; /** @@ -49,7 +49,7 @@ export type UsePickerProps = UsePickerDerivedProps & UsePickerPassthroughProps; export function usePickerProps({ - table, + table: tableSource, keyColumn: keyColumnName, labelColumn: labelColumnName, iconColumn: iconColumnName, @@ -69,13 +69,35 @@ export function usePickerProps({ ItemKey | null | undefined >(props.defaultSelectedKey); + // Copy table so we can apply filters without affecting the original table. + // (Note that this call is not actually applying any filters. Filter will be + // applied in `useSearchableViewportData`.) + const { data: tableCopy } = usePromiseFactory( + TableUtils.copyTableAndApplyFilters, + [tableSource] + ); + const keyColumn = useMemo( - () => getItemKeyColumn(table, keyColumnName), - [keyColumnName, table] + () => + tableCopy == null ? null : getItemKeyColumn(tableCopy, keyColumnName), + [keyColumnName, tableCopy] + ); + + const labelColumn = useMemo( + () => + tableCopy == null || keyColumn == null + ? null + : getItemLabelColumn(tableCopy, keyColumn, labelColumnName), + [keyColumn, labelColumnName, tableCopy] + ); + + const searchColumnNames = useMemo( + () => (labelColumn == null ? [] : [labelColumn.name]), + [labelColumn] ); const deserializeRow = useItemRowDeserializer({ - table, + table: tableCopy, iconColumnName, keyColumnName, labelColumnName, @@ -83,8 +105,8 @@ export function usePickerProps({ }); const getItemIndexByValue = useGetItemIndexByValue({ - table, - columnName: keyColumn.name, + table: tableCopy, + columnName: keyColumn?.name ?? null, value: isUncontrolled ? uncontrolledSelectedKey : props.selectedKey, }); @@ -98,15 +120,14 @@ export function usePickerProps({ return index * itemHeight + PICKER_TOP_OFFSET; }, [getItemIndexByValue, itemHeight]); - const { viewportData, onScroll, setViewport } = useViewportData< - NormalizedItemData | NormalizedSectionData, - DhType.Table - >({ - reuseItemsOnTableResize: true, - table, - itemHeight, - deserializeRow, - }); + const { onScroll, onSearchTextChange, setViewport, viewportData } = + useSearchableViewportData({ + reuseItemsOnTableResize: true, + table: tableCopy, + itemHeight, + deserializeRow, + searchColumnNames, + }); const normalizedItems = viewportData.items as ( | NormalizedItem @@ -158,6 +179,7 @@ export function usePickerProps({ getInitialScrollPosition, onChange: onSelectionChangeInternal, onScroll, + onSearchTextChange, }; }