From b05d8fb6c5389d53390aad25d5fc49db4a5c328e Mon Sep 17 00:00:00 2001 From: lbwexler Date: Thu, 25 Jun 2026 09:23:08 -0400 Subject: [PATCH 1/4] Add popover mode to FilterChooser; deprecate PopoverFilterChooser (#4084) Folds the popover behavior into FilterChooser as a first-class `popover` mode backed by a single real `select`, replacing the PopoverFilterChooser wrapper's duplicate-control design (two filterChoosers sharing one inputRef, an `omit` hack, and a `disabled` target) that caused all three reported symptoms: - The collapsed trigger now always occupies its space, so surrounding layout no longer shifts when the popover opens. - The trigger's clear and favorites controls respond to a single click rather than first requiring the popover to be opened. Only the interactive instance binds `model.inputRef`, so trigger and content can mount together without contention. The trigger is made non-interactive via react-select `menuIsOpen`/`isSearchable` rather than `disabled`, keeping its clear/favorites affordances live; the expand popover skips opening on clicks that land on those controls. PopoverFilterChooser is now a thin @deprecated alias delegating to `filterChooser({popover: true})`. Admin panels migrated accordingly. --- CHANGELOG.md | 8 + .../tracking/ActivityTrackingPanel.ts | 5 +- admin/tabs/userData/roles/RolePanel.ts | 4 +- desktop/cmp/filter/FilterChooser.scss | 27 +++ desktop/cmp/filter/FilterChooser.ts | 167 +++++++++++++++++- desktop/cmp/filter/PopoverFilterChooser.scss | 42 ----- desktop/cmp/filter/PopoverFilterChooser.ts | 91 +--------- 7 files changed, 206 insertions(+), 138 deletions(-) delete mode 100644 desktop/cmp/filter/PopoverFilterChooser.scss diff --git a/CHANGELOG.md b/CHANGELOG.md index ffecb2be79..00a154b3d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,9 +19,17 @@ * Added a `lossless` option to `fmtQuantity` to compact values to millions / billions units only when doing so loses no precision, rendering the full value otherwise (e.g. `7,100,100` stays `7,100,100` rather than collapsing to `7.10m`). +* `FilterChooser` now supports a `popover` mode (`filterChooser({popover: true})`) that renders the + control collapsed in-place and expands it into a popover when opened - ideal for toolbars, where + it can grow vertically without disrupting surrounding layout. This supersedes the now-deprecated + `PopoverFilterChooser` component, which remains as a thin alias. ### 🐞 Bug Fixes +* Fixed `FilterChooser` popover mode (formerly `PopoverFilterChooser`) so its collapsed control no + longer disappears when opened - it now always occupies its place in the layout, so surrounding + elements no longer shift. Its clear and favorites controls also respond to a single click rather + than requiring the popover to be opened first. * Fixed grid `NumberEditor` to allow starting an edit by typing `-`, `+`, or `.` (e.g. to enter a negative or decimal value), while reliably rejecting other non-numeric keypresses. * `fmtNumber` now treats a `precision` of `null` as full, unrestricted precision rather than diff --git a/admin/tabs/activity/tracking/ActivityTrackingPanel.ts b/admin/tabs/activity/tracking/ActivityTrackingPanel.ts index 182a34d22e..72dba19484 100644 --- a/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +++ b/admin/tabs/activity/tracking/ActivityTrackingPanel.ts @@ -11,7 +11,7 @@ import {grid} from '@xh/hoist/cmp/grid'; import {div, filler, hframe} from '@xh/hoist/cmp/layout'; import {creates, hoistCmp} from '@xh/hoist/core'; import {button, buttonGroup, colChooserButton, exportButton} from '@xh/hoist/desktop/cmp/button'; -import {popoverFilterChooser} from '@xh/hoist/desktop/cmp/filter'; +import {filterChooser} from '@xh/hoist/desktop/cmp/filter'; import {formField} from '@xh/hoist/desktop/cmp/form'; import {groupingChooser} from '@xh/hoist/desktop/cmp/grouping'; import {dateInput, DateInputProps, select} from '@xh/hoist/desktop/cmp/input'; @@ -137,7 +137,8 @@ const filterChooserToggleButton = hoistCmp.factory(({mode const filterBar = hoistCmp.factory(({model}) => { return model.showFilterChooser ? toolbar( - popoverFilterChooser({ + filterChooser({ + popover: true, flex: 1, enableClear: true }) diff --git a/admin/tabs/userData/roles/RolePanel.ts b/admin/tabs/userData/roles/RolePanel.ts index 4487a6c005..2db22fb265 100644 --- a/admin/tabs/userData/roles/RolePanel.ts +++ b/admin/tabs/userData/roles/RolePanel.ts @@ -10,7 +10,7 @@ import {fragment, hframe, vframe} from '@xh/hoist/cmp/layout'; import {creates, hoistCmp} from '@xh/hoist/core'; import {button, colChooserButton} from '@xh/hoist/desktop/cmp/button'; import {errorMessage} from '@xh/hoist/cmp/error'; -import {popoverFilterChooser} from '@xh/hoist/desktop/cmp/filter'; +import {filterChooser} from '@xh/hoist/desktop/cmp/filter'; import {switchInput} from '@xh/hoist/desktop/cmp/input'; import {panel} from '@xh/hoist/desktop/cmp/panel'; import {recordActionBar} from '@xh/hoist/desktop/cmp/record'; @@ -44,7 +44,7 @@ export const rolePanel = hoistCmp.factory({ selModel: gridModel.selModel }), '-', - popoverFilterChooser({flex: 1}), + filterChooser({popover: true, flex: 1}), '-', switchInput({ bind: 'showInGroups', diff --git a/desktop/cmp/filter/FilterChooser.scss b/desktop/cmp/filter/FilterChooser.scss index dc134ce82e..1a6d3d5ff2 100644 --- a/desktop/cmp/filter/FilterChooser.scss +++ b/desktop/cmp/filter/FilterChooser.scss @@ -15,6 +15,33 @@ margin: 7px 4px; align-self: start; } + + // Popover mode - collapsed trigger expands into an overlaid popover. + &--popover > .bp6-popover-target { + display: flex; + flex: 1; + } + + &__trigger { + .xh-select__value-container--is-multi { + overflow-y: hidden !important; + } + } + + // Extra class names required to override the default styles of the popover. + &__popover.bp6-popover.bp6-minimal { + margin-top: -15px !important; + box-shadow: none; + + .bp6-popover-content { + background: transparent; + } + + .xh-select__value-container--is-multi { + height: unset; + line-height: unset; + } + } } .xh-filter-chooser-option { diff --git a/desktop/cmp/filter/FilterChooser.ts b/desktop/cmp/filter/FilterChooser.ts index dc69ffb355..daca60574f 100644 --- a/desktop/cmp/filter/FilterChooser.ts +++ b/desktop/cmp/filter/FilterChooser.ts @@ -6,20 +6,36 @@ */ import {FilterChooserFilter, FilterChooserModel} from '@xh/hoist/cmp/filter'; import {box, div, hbox, hframe, vbox} from '@xh/hoist/cmp/layout'; -import {hoistCmp, HoistProps, LayoutProps, uses} from '@xh/hoist/core'; +import { + hoistCmp, + HoistModel, + HoistProps, + LayoutProps, + lookup, + useLocalModel, + uses +} from '@xh/hoist/core'; +import {bindable, makeObservable} from '@xh/hoist/mobx'; import {button} from '@xh/hoist/desktop/cmp/button'; import {select} from '@xh/hoist/desktop/cmp/input'; import '@xh/hoist/desktop/register'; import {Icon} from '@xh/hoist/icon'; import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint'; -import {withDefault} from '@xh/hoist/utils/js'; -import {splitLayoutProps} from '@xh/hoist/utils/react'; +import {elemWithin, withDefault} from '@xh/hoist/utils/js'; +import {getLayoutProps, splitLayoutProps} from '@xh/hoist/utils/react'; import classNames from 'classnames'; -import {isEmpty, sortBy} from 'lodash'; +import {isEmpty, isObject, sortBy} from 'lodash'; import {badge} from '@xh/hoist/cmp/badge'; import {cloneElement, ReactElement} from 'react'; import './FilterChooser.scss'; +export interface FilterChooserPopoverOptions { + /** Side of the trigger on which to expand the popover. Defaults to 'bottom'. */ + position?: 'bottom' | 'top'; + /** Width in pixels of the expanded popover - defaults to matching the trigger width. */ + width?: number; +} + export interface FilterChooserProps extends HoistProps, LayoutProps { /** True to focus the control on render. */ autoFocus?: boolean; @@ -41,6 +57,13 @@ export interface FilterChooserProps extends HoistProps, Layo placeholder?: string; /** Icon clicked to launch favorites menu. (Defaults to Icon.favorite()) */ favoritesIcon?: ReactElement; + /** + * True (or an options object) to render the control collapsed in-place, expanding into a + * popover when opened. Useful within height-constrained containers such as toolbars, where + * the chooser can then grow vertically without disrupting surrounding layout. The collapsed + * trigger still supports single-click clearing and favorites access. + */ + popover?: boolean | FilterChooserPopoverOptions; } /** @@ -50,6 +73,30 @@ export interface FilterChooserProps extends HoistProps, Layo export const [FilterChooser, filterChooser] = hoistCmp.withFactory({ model: uses(FilterChooserModel), className: 'xh-filter-chooser', + render({model, className, ...props}, ref) { + return props.popover + ? popoverFilterChooser({model, className, ...props, ref}) + : filterChooserControl({model, className, ...props, ref}); + } +}); + +//------------------ +// Implementation +//------------------ +interface FilterChooserControlProps extends FilterChooserProps { + /** Internal - false to render a non-interactive display (the collapsed popover trigger). */ + xhInteractive?: boolean; + /** Internal - override for the favorites menu open state. */ + xhFavoritesOpen?: boolean; +} + +/** + * The Select-based control shared by the inline FilterChooser and both faces (collapsed trigger and + * expanded content) of the popover variant. Only the interactive instance binds `model.inputRef`, + * so the popover can mount trigger and content simultaneously without contention. + */ +const filterChooserControl = hoistCmp.factory({ + model: uses(FilterChooserModel), render({model, className, ...props}, ref) { const [layoutProps, chooserProps] = splitLayoutProps(props), { @@ -68,7 +115,9 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory favoritesIconCmp(model, favoritesIcon) - } + }, + // Display-only trigger: suppress menu + typing, but leave clear and + // favorites affordances live for single-click access. + ...(xhInteractive ? {} : {menuIsOpen: false, isSearchable: false}) } }) ), content: favoritesMenu(), - isOpen: favoritesIsOpen, + isOpen: xhFavoritesOpen ?? favoritesIsOpen, position: 'bottom-right', minimal: true, onInteraction: willOpen => { @@ -136,6 +189,104 @@ export const [FilterChooser, filterChooser] = hoistCmp.withFactory({ + model: uses(FilterChooserModel), + render({model, className, ...props}, ref) { + const impl = useLocalModel(FilterChooserLocalModel), + {popoverIsOpen} = impl, + {popover: popoverSpec, ...rest} = props, + opts = isObject(popoverSpec) ? popoverSpec : {}, + layoutProps = getLayoutProps(rest); + + return box({ + ref, + className: classNames(className, 'xh-filter-chooser--popover'), + ...layoutProps, + item: popover({ + isOpen: popoverIsOpen, + popoverClassName: 'xh-filter-chooser__popover', + matchTargetWidth: true, + minimal: true, + position: opts.position ?? 'bottom', + item: filterChooserControl({ + model, + flex: 1, + className: 'xh-filter-chooser__trigger', + ...rest, + displayCount: true, + xhInteractive: false, + // Trigger hosts the favorites menu only while collapsed; once open, the + // expanded content takes over. + xhFavoritesOpen: model.favoritesIsOpen && !popoverIsOpen + }), + content: filterChooserControl({ + model, + flex: 1, + width: opts.width, + className: 'xh-filter-chooser__content', + ...rest, + displayCount: true + }), + onInteraction: (willOpen, e) => { + if (willOpen) { + // Let clicks on the inline clear / favorites controls act directly. + const target = e?.target as HTMLElement; + if ( + target && + (elemWithin(target, 'xh-select__clear-indicator') || + elemWithin(target, 'xh-filter-chooser-favorite-icon')) + ) { + return; + } + impl.open(); + } else { + impl.close(); + } + } + }) + }); + } +}); + +class FilterChooserLocalModel extends HoistModel { + override xhImpl = true; + + @lookup(FilterChooserModel) + model: FilterChooserModel; + + @bindable + popoverIsOpen: boolean = false; + + constructor() { + super(); + makeObservable(this); + } + + open() { + this.popoverIsOpen = true; + + // Focus the (now-mounted) interactive input and open its menu once available. + this.addReaction({ + when: () => !!this.model.inputRef.current, + run: () => { + const inputRef = this.model.inputRef.current; + inputRef.focus(); + (inputRef as any).reactSelectRef.current?.openMenu('first'); + } + }); + } + + close() { + this.popoverIsOpen = false; + } +} + //----------------- // Options //------------------ diff --git a/desktop/cmp/filter/PopoverFilterChooser.scss b/desktop/cmp/filter/PopoverFilterChooser.scss deleted file mode 100644 index d044a0a98f..0000000000 --- a/desktop/cmp/filter/PopoverFilterChooser.scss +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file belongs to Hoist, an application development toolkit - * developed by Extremely Heavy Industries (www.xh.io | info@xh.io) - * - * Copyright © 2026 Extremely Heavy Industries Inc. - */ -.xh-popover-filter-chooser { - & > .bp6-popover-target { - display: flex; - flex: 1; - } - - // Extra class names required to override the default styles of the popover - &__popover.bp6-popover.bp6-minimal { - margin-top: -15px !important; - box-shadow: none; - - .bp6-popover-content { - background: transparent; - } - - .xh-select__value-container--is-multi { - height: unset; - line-height: unset; - } - } - - &__filter-chooser { - .xh-select__value-container--is-multi { - overflow-y: hidden !important; - } - - .xh-select { - .xh-select__control--is-disabled { - background: var(--xh-input-bg); - } - .xh-select__multi-value--is-disabled .xh-select__multi-value__label { - color: unset; - } - } - } -} diff --git a/desktop/cmp/filter/PopoverFilterChooser.ts b/desktop/cmp/filter/PopoverFilterChooser.ts index e03b351c67..5c4aedf4fb 100644 --- a/desktop/cmp/filter/PopoverFilterChooser.ts +++ b/desktop/cmp/filter/PopoverFilterChooser.ts @@ -5,100 +5,23 @@ * Copyright © 2026 Extremely Heavy Industries Inc. */ -import {hoistCmp, HoistModel, lookup, useLocalModel, uses} from '@xh/hoist/core'; -import {bindable, makeObservable} from '@xh/hoist/mobx'; -import {box, hframe} from '@xh/hoist/cmp/layout'; +import {hoistCmp, uses} from '@xh/hoist/core'; import '@xh/hoist/desktop/register'; -import {popover} from '@xh/hoist/kit/blueprint'; -import {getLayoutProps} from '@xh/hoist/utils/react'; -import './PopoverFilterChooser.scss'; -import {filterChooser, FilterChooserProps} from './FilterChooser'; import {FilterChooserModel} from '@xh/hoist/cmp/filter'; +import {filterChooser, FilterChooserProps} from './FilterChooser'; /** * A wrapper around a FilterChooser that renders in a popover when opened, allowing it to expand * vertically beyond the height of a toolbar. + * + * @deprecated Use `filterChooser({popover: true})` instead - the popover behavior is now a built-in + * mode of `FilterChooser`. This alias will be removed in a future major release. * @see FilterChooser */ export const [PopoverFilterChooser, popoverFilterChooser] = hoistCmp.withFactory({ model: uses(FilterChooserModel), - className: 'xh-popover-filter-chooser', - render({model, className, ...props}, ref) { - const layoutProps = getLayoutProps(props), - impl = useLocalModel(PopoverFilterChooserLocalModel); - - return box({ - ref, - className, - ...layoutProps, - item: popover({ - isOpen: impl.popoverIsOpen, - popoverClassName: 'xh-popover-filter-chooser__popover', - item: hframe( - filterChooser({ - model, - // Omit when popover is open to force update the inputRef - omit: impl.popoverIsOpen, - className: 'xh-popover-filter-chooser__filter-chooser', - displayCount: true, - ...props, - disabled: true - }) - ), - content: filterChooser({ - model, - displayCount: true, - ...props - }), - matchTargetWidth: true, - minimal: true, - position: 'bottom', - onInteraction: open => { - if (open) { - impl.open(); - } else { - impl.close(); - } - } - }) - }); + render(props) { + return filterChooser({popover: true, ...props}); } }); - -class PopoverFilterChooserLocalModel extends HoistModel { - override xhImpl = true; - - @lookup(FilterChooserModel) - model: FilterChooserModel; - - @bindable - popoverIsOpen: boolean = false; - - get displaySelectValue() { - return this.model.selectValue[0]; - } - - constructor() { - super(); - makeObservable(this); - } - - open() { - this.popoverIsOpen = true; - - // Focus and open the menu when rendered - this.addReaction({ - when: () => !!this.model.inputRef.current, - run: () => { - const inputRef = this.model.inputRef.current; - inputRef.focus(); - (inputRef as any).reactSelectRef.current?.openMenu('first'); - } - }); - } - - close() { - this.popoverIsOpen = false; - } -} From 4151b4d2a740d087213d163e4b837a3ca507918e Mon Sep 17 00:00:00 2001 From: lbwexler Date: Wed, 1 Jul 2026 14:27:58 -0400 Subject: [PATCH 2/4] Checkpoint --- CHANGELOG.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 317529c061..013b7272cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ * Hardened the grid column filter's Custom tab against filters it previously mishandled - multi-value clauses are now expanded into editable rows and recombined on commit, and filters it cannot represent are left untouched rather than corrupted. +* Fixed `FilterChooser` popover mode (formerly `PopoverFilterChooser`) so its collapsed control no + longer disappears when opened - it now always occupies its place in the layout, so surrounding + elements no longer shift. Its clear and favorites controls also respond to a single click rather + than requiring the popover to be opened first. This mode is now enabled more naturally via + a new option `filterChooser({popover: true})`, deprecating `PopoverFilterChooser`, which remains + as a thin alias. ### ⚙️ Typescript API Adjustments @@ -47,17 +53,9 @@ * Added a `lossless` option to `fmtQuantity` to compact values to millions / billions units only when doing so loses no precision, rendering the full value otherwise (e.g. `7,100,100` stays `7,100,100` rather than collapsing to `7.10m`). -* `FilterChooser` now supports a `popover` mode (`filterChooser({popover: true})`) that renders the - control collapsed in-place and expands it into a popover when opened - ideal for toolbars, where - it can grow vertically without disrupting surrounding layout. This supersedes the now-deprecated - `PopoverFilterChooser` component, which remains as a thin alias. ### 🐞 Bug Fixes -* Fixed `FilterChooser` popover mode (formerly `PopoverFilterChooser`) so its collapsed control no - longer disappears when opened - it now always occupies its place in the layout, so surrounding - elements no longer shift. Its clear and favorites controls also respond to a single click rather - than requiring the popover to be opened first. * Fixed grid `NumberEditor` to allow starting an edit by typing `-`, `+`, or `.` (e.g. to enter a negative or decimal value), while reliably rejecting other non-numeric keypresses. * `fmtNumber` now treats a `precision` of `null` as full, unrestricted precision rather than From e6df91ae355e3c71269968fa30286ebb51918cb8 Mon Sep 17 00:00:00 2001 From: lbwexler Date: Wed, 1 Jul 2026 14:41:08 -0400 Subject: [PATCH 3/4] Slim-down new API -- remove speculative options --- desktop/cmp/filter/FilterChooser.ts | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/desktop/cmp/filter/FilterChooser.ts b/desktop/cmp/filter/FilterChooser.ts index daca60574f..c03fa689a0 100644 --- a/desktop/cmp/filter/FilterChooser.ts +++ b/desktop/cmp/filter/FilterChooser.ts @@ -24,18 +24,11 @@ import {menu, menuDivider, menuItem, popover} from '@xh/hoist/kit/blueprint'; import {elemWithin, withDefault} from '@xh/hoist/utils/js'; import {getLayoutProps, splitLayoutProps} from '@xh/hoist/utils/react'; import classNames from 'classnames'; -import {isEmpty, isObject, sortBy} from 'lodash'; +import {isEmpty, sortBy} from 'lodash'; import {badge} from '@xh/hoist/cmp/badge'; import {cloneElement, ReactElement} from 'react'; import './FilterChooser.scss'; -export interface FilterChooserPopoverOptions { - /** Side of the trigger on which to expand the popover. Defaults to 'bottom'. */ - position?: 'bottom' | 'top'; - /** Width in pixels of the expanded popover - defaults to matching the trigger width. */ - width?: number; -} - export interface FilterChooserProps extends HoistProps, LayoutProps { /** True to focus the control on render. */ autoFocus?: boolean; @@ -58,12 +51,10 @@ export interface FilterChooserProps extends HoistProps, Layo /** Icon clicked to launch favorites menu. (Defaults to Icon.favorite()) */ favoritesIcon?: ReactElement; /** - * True (or an options object) to render the control collapsed in-place, expanding into a - * popover when opened. Useful within height-constrained containers such as toolbars, where - * the chooser can then grow vertically without disrupting surrounding layout. The collapsed - * trigger still supports single-click clearing and favorites access. + * True to render collapsed in-place, expanding into a popover when opened - useful in toolbars + * and other height-constrained containers. */ - popover?: boolean | FilterChooserPopoverOptions; + popover?: boolean; } /** @@ -200,8 +191,7 @@ const popoverFilterChooser = hoistCmp.factory({ render({model, className, ...props}, ref) { const impl = useLocalModel(FilterChooserLocalModel), {popoverIsOpen} = impl, - {popover: popoverSpec, ...rest} = props, - opts = isObject(popoverSpec) ? popoverSpec : {}, + {popover: _popover, ...rest} = props, layoutProps = getLayoutProps(rest); return box({ @@ -213,7 +203,7 @@ const popoverFilterChooser = hoistCmp.factory({ popoverClassName: 'xh-filter-chooser__popover', matchTargetWidth: true, minimal: true, - position: opts.position ?? 'bottom', + position: 'bottom', item: filterChooserControl({ model, flex: 1, @@ -228,7 +218,6 @@ const popoverFilterChooser = hoistCmp.factory({ content: filterChooserControl({ model, flex: 1, - width: opts.width, className: 'xh-filter-chooser__content', ...rest, displayCount: true From 096c609b5a659c85799c73313c7bf736ed943c7c Mon Sep 17 00:00:00 2001 From: lbwexler Date: Wed, 1 Jul 2026 17:09:12 -0400 Subject: [PATCH 4/4] Checkpoint -- fixes from testing --- desktop/cmp/filter/FilterChooser.scss | 16 +++++++++++++++- desktop/cmp/filter/FilterChooser.ts | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/desktop/cmp/filter/FilterChooser.scss b/desktop/cmp/filter/FilterChooser.scss index 1a6d3d5ff2..56e23edee8 100644 --- a/desktop/cmp/filter/FilterChooser.scss +++ b/desktop/cmp/filter/FilterChooser.scss @@ -30,9 +30,23 @@ // Extra class names required to override the default styles of the popover. &__popover.bp6-popover.bp6-minimal { - margin-top: -15px !important; box-shadow: none; + // Overlay the expanded input onto the collapsed 30px trigger. Offset follows Blueprint's + // resolved placement, which flips (bottom-anchored) near the viewport's bottom edge. + &.bp6-popover-placement-bottom { + margin-top: -30px !important; + } + &.bp6-popover-placement-top { + margin-bottom: -30px !important; + } + + // Content is portaled out of .xh-filter-chooser - restate the flex rule so it fills the width. + .bp6-popover-target { + display: flex; + flex: 1; + } + .bp6-popover-content { background: transparent; } diff --git a/desktop/cmp/filter/FilterChooser.ts b/desktop/cmp/filter/FilterChooser.ts index c03fa689a0..36b3468d3b 100644 --- a/desktop/cmp/filter/FilterChooser.ts +++ b/desktop/cmp/filter/FilterChooser.ts @@ -52,7 +52,7 @@ export interface FilterChooserProps extends HoistProps, Layo favoritesIcon?: ReactElement; /** * True to render collapsed in-place, expanding into a popover when opened - useful in toolbars - * and other height-constrained containers. + * and other height-constrained containers. Opens in the direction set by `menuPlacement`. */ popover?: boolean; }