Problem
EuiSearchBar.onChange is invoked from two internal entry points — onSearch (free-text typing) and onFiltersChange (a custom_component filter calling its provided onChange) — but both funnel through notifyControllingParent, which only forwards { query, queryText, error } to the consumer. The consumer cannot tell which path produced the change.
Use case
In @kbn/content-list-toolbar, Content List drives URL persistence via history.push vs history.replace:
- Typing keystrokes →
history.replace (don't create one history entry per character).
- Committed filter actions →
history.push (Back/Forward should move between filter states).
Because EUI doesn't expose the source, the toolbar currently has to wrap each custom_component filter in a tiny HOC that intercepts onChange before EUI sees it, dispatches with source: 'filter', and bypasses EUI's controlled-state notification entirely. A WeakMap<Component, WrappedComponent> cache is needed because CustomComponentFilter renders config.component directly, so a fresh wrapper per recompute would unmount/remount each filter's internals (popover state, debounce timers, in-flight facet queries).
Proposal
Plumb a source discriminator through notifyControllingParent into the consumer's onChange:
onChange?: (args: {
query: Query | null;
queryText: string;
error: Error | null;
source: 'search' | 'filters'; // new
}) => void;
Alternatively, expose a separate onFiltersChange prop so the typing path and filter path stay distinct from the top.
Either change would let @kbn/content-list-toolbar (and any other consumer that cares about intent) drop the per-component wrapper and the WeakMap cache, collapsing use_filters.ts to a single handleSearchChange that branches on args.source.
Workaround in tree
See wrapperCache / getWrappedFilterComponent / wrapCustomFilters in use_filters.ts, and the originating discussion at elastic/kibana#267632 (comment).
Problem
EuiSearchBar.onChangeis invoked from two internal entry points —onSearch(free-text typing) andonFiltersChange(acustom_componentfilter calling its providedonChange) — but both funnel throughnotifyControllingParent, which only forwards{ query, queryText, error }to the consumer. The consumer cannot tell which path produced the change.Use case
In
@kbn/content-list-toolbar, Content List drives URL persistence viahistory.pushvshistory.replace:history.replace(don't create one history entry per character).history.push(Back/Forward should move between filter states).Because EUI doesn't expose the source, the toolbar currently has to wrap each
custom_componentfilter in a tiny HOC that interceptsonChangebefore EUI sees it, dispatches withsource: 'filter', and bypasses EUI's controlled-state notification entirely. AWeakMap<Component, WrappedComponent>cache is needed becauseCustomComponentFilterrendersconfig.componentdirectly, so a fresh wrapper per recompute would unmount/remount each filter's internals (popover state, debounce timers, in-flight facet queries).Proposal
Plumb a
sourcediscriminator throughnotifyControllingParentinto the consumer'sonChange:Alternatively, expose a separate
onFiltersChangeprop so the typing path and filter path stay distinct from the top.Either change would let
@kbn/content-list-toolbar(and any other consumer that cares about intent) drop the per-component wrapper and theWeakMapcache, collapsinguse_filters.tsto a singlehandleSearchChangethat branches onargs.source.Workaround in tree
See
wrapperCache/getWrappedFilterComponent/wrapCustomFiltersinuse_filters.ts, and the originating discussion at elastic/kibana#267632 (comment).