Skip to content

Commit

Permalink
Fix filters from being removed when searching
Browse files Browse the repository at this point in the history
Tippy unmounts its popper element from the DOM on hide, which means that
the filter fields get detached from the form. As a result, when
performing a search (which means the filters popup is closed), any
filters that have been applied will be lost. The issue does not occur
the other way around (filtering after search) because the filter fields
will still be in the DOM (as the popup is open while you're applying the
filters).

Unfortunately, Tippy does not offer a built-in option to keep the popper
mounted in the DOM when it's hidden.

As a workaround, use Tippy's hooks to re-append the popper to the DOM.
- onCreate: ensure the popper element is mounted even when the tippy was
  just created (normally it's only mounted when shown). This is useful
  to keep the filters when searching after a full-page refresh (e.g.
  after navigating to the next page in pagination).
- onHidden: ensure the popper is still mounted, to solve the main issue
- onShow: remove the hidden attribute and let tippy move the popper
  element as necessary (though usually this isn't necessary, because we
  remount it in the same position (the controller's element).

The 'hidden' attribute shouldn't cause any issues here as Tippy doesn't
use it. Without it, the solution still works because Tippy uses CSS to
make the element invisible. However, the complete CSS doesn't get
applied until the tippy is shown at least once. This means on initial
load with the onCreate hook, the tippy will take up the space while
being invisible. Using the attribute fixes it (and display:none probably
would too).

This behaviour can be enabled using the newly-added DropdownController
value 'keepMounted'.
  • Loading branch information
laymonage committed Jan 26, 2024
1 parent 6991c1f commit a3cb4a6
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 7 deletions.
34 changes: 29 additions & 5 deletions client/src/controllers/DropdownController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,22 @@ export class DropdownController extends Controller<HTMLElement> {
static targets = ['toggle', 'content'];
static values = {
hideOnClick: { default: false, type: Boolean },
keepMounted: { default: false, type: Boolean },
offset: Array,
theme: { default: 'dropdown' as TippyTheme, type: String },
};

// Hide on click *inside* the dropdown. Differs from tippy's hideOnClick
// option for outside clicks that defaults to true and we don't yet expose it.
declare hideOnClickValue: boolean;
declare offsetValue: [number, number];
declare readonly hideOnClickValue: boolean;
declare readonly keepMountedValue: boolean;
declare readonly offsetValue: [number, number];
declare readonly hasOffsetValue: boolean;
declare readonly themeValue: TippyTheme;

declare readonly contentTarget: HTMLDivElement;
declare readonly hasContentTarget: boolean;
declare readonly hasOffsetValue: boolean;
declare readonly toggleTarget: HTMLButtonElement;
declare readonly themeValue: TippyTheme;

tippy?: Instance<Props>;

Expand Down Expand Up @@ -144,10 +146,22 @@ export class DropdownController extends Controller<HTMLElement> {
});
}

const onShow = () => {
const onCreate = (instance: Instance<Props>) => {
if (this.keepMountedValue) {
const { popper } = instance;
this.element.append(popper);
popper.hidden = true;
}
};

const onShow = (instance: Instance<Props>) => {
if (hoverTooltipInstance) {
hoverTooltipInstance.disable();
}
if (this.keepMountedValue) {
const { popper } = instance;
popper.hidden = false;
}
};

const onShown = () => {
Expand All @@ -161,6 +175,14 @@ export class DropdownController extends Controller<HTMLElement> {
}
};

const onHidden = (instance: Instance<Props>) => {
if (this.keepMountedValue) {
const { popper } = instance;
this.element.append(popper);
popper.hidden = true;
}
};

return {
...(this.hasContentTarget
? { content: this.contentTarget as Content }
Expand All @@ -171,9 +193,11 @@ export class DropdownController extends Controller<HTMLElement> {
...(this.hasOffsetValue && { offset: this.offsetValue }),
getReferenceClientRect: () => this.reference.getBoundingClientRect(),
theme: this.themeValue,
onCreate,
onShow,
onShown,
onHide,
onHidden,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,21 @@
- `toggle_classname` (string?) - additional toggle classes
- `toggle_tooltip_offset` (string?) - Tooltip offset prop
- `hide_on_click` (boolean?) - Whether or not the tooltip should hide when clicking inside
- `keep_mounted` (boolean?) - Whether or not the tooltip should keep its DOM node mounted when hidden
- `children` - Dropdown contents (`a` and `button` elements only)
{% endcomment %}

{% fragment as class %}{% classnames 'w-dropdown' classname %}{% if theme %} w-dropdown--{{ theme }}{% endif %}{% endfragment %}

<div data-controller="w-dropdown" {% if theme %}data-w-dropdown-theme-value="{{ theme }}"{% endif %} class="{{ class }}" {{ attrs }} {% if hide_on_click %}data-w-dropdown-hide-on-click-value="true"{% endif %}{% if toggle_tooltip_offset %} data-w-dropdown-offset-value="{{ toggle_tooltip_offset }}"{% endif %}>
<div
data-controller="w-dropdown"
class="{{ class }}"
{{ attrs }}
{% if theme %}data-w-dropdown-theme-value="{{ theme }}"{% endif %}
{% if toggle_tooltip_offset %}data-w-dropdown-offset-value="{{ toggle_tooltip_offset }}"{% endif %}
{% if hide_on_click %}data-w-dropdown-hide-on-click-value="true"{% endif %}
{% if keep_mounted %}data-w-dropdown-keep-mounted-value="true"{% endif %}
>
<button type="button" class="{% classnames 'w-dropdown__toggle' toggle_label|yesno:',w-dropdown__toggle--icon' toggle_classname %}" data-w-dropdown-target="toggle"{% if toggle_aria_label %} aria-label="{{ toggle_aria_label }}"{% endif %}{% if toggle_describedby %} aria-describedby="{{ toggle_describedby }}"{% endif %}{% if toggle_tippy_offset %} data-tippy-offset="{{ toggle_tippy_offset }}"{% endif %}>
{{ toggle_label }}
{% if toggle_icon %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<span class="w-drilldown__count" data-w-drilldown-target="count" hidden></span>
{% endfragment %}

{% dropdown theme="drilldown" toggle_icon="sliders" toggle_classname="w-filter-button" toggle_aria_label=_("Show filters") toggle_suffix=toggle_suffix %}
{% dropdown theme="drilldown" toggle_icon="sliders" toggle_classname="w-filter-button" toggle_aria_label=_("Show filters") toggle_suffix=toggle_suffix keep_mounted=True %}
<div class="w-drilldown__contents">
<div class="w-drilldown__menu" data-w-drilldown-target="menu">
<h2 class="w-help-text w-pl-5 w-py-2.5 w-my-0">{% trans "Filter by" %}</h2>
Expand Down

0 comments on commit a3cb4a6

Please sign in to comment.