From e8b34ededd5196a2009032daf29c14b28be8a495 Mon Sep 17 00:00:00 2001 From: Sage Abdullah Date: Tue, 23 Jan 2024 23:58:15 +0000 Subject: [PATCH] Implement active filter button to open drilldown for specific filter --- client/scss/components/_pill.scss | 6 +- client/src/controllers/DrilldownController.ts | 55 +++++++++++++++++++ .../wagtailadmin/shared/active_filters.html | 6 +- .../shared/headers/slim_header.html | 10 +++- 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/client/scss/components/_pill.scss b/client/scss/components/_pill.scss index 70ec41efef..75f4501648 100644 --- a/client/scss/components/_pill.scss +++ b/client/scss/components/_pill.scss @@ -7,6 +7,10 @@ } &__remove { - @apply w-pr-3 w-py-1 w-rounded-r-xl w-border w-border-info-100 w-bg-info-100 hover:w-bg-info-125 w-text-white hover:w-text-white w-p-0 w-pl-2 w-flex w-items-center w-justify-center w-h-full; + @apply w-pr-3 w-py-1 w-rounded-r-xl w-border w-border-info-100 w-bg-info-100 w-text-white w-p-0 w-pl-2 w-flex w-items-center w-justify-center w-h-full; + } + + button { + @apply hover:w-bg-info-125; } } diff --git a/client/src/controllers/DrilldownController.ts b/client/src/controllers/DrilldownController.ts index a227b03e7d..f1c4886e8b 100644 --- a/client/src/controllers/DrilldownController.ts +++ b/client/src/controllers/DrilldownController.ts @@ -1,4 +1,6 @@ import { Controller } from '@hotwired/stimulus'; +import { ActionController } from './ActionController'; +import { DropdownController } from './DropdownController'; /** * Drilldown menu interaction combined with URL-driven @@ -13,17 +15,44 @@ export class DrilldownController extends Controller { countAttr: { default: '', type: String }, }; + static outlets = [ + // w-action outlet for submenu toggles that are outside the drilldown. + // We don't really use anything specific to ActionController, we just need + // a Stimulus controller to be able to use the outlet. As with toggle targets, + // these need to have aria-controls. + 'w-action', + + // w-dropdown outlet for the popup menu, to allow interacting with the + // DropdownController programmatically. + 'w-dropdown', + ]; + declare activeSubmenuValue: string; declare countAttrValue: string; declare readonly countTargets: HTMLElement[]; declare readonly menuTarget: HTMLElement; declare readonly toggleTargets: HTMLButtonElement[]; + declare readonly hasWDropdownOutlet: boolean; + declare readonly wActionOutlets: ActionController[]; + declare readonly wDropdownOutlet: DropdownController; countTargetConnected() { this.updateCount(); } + connect(): void { + this.open = this.open.bind(this); + } + + wActionOutletConnected(_: ActionController, element: HTMLElement) { + element.addEventListener('click', this.open); + } + + wActionOutletDisconnected(_: ActionController, element: HTMLElement) { + element.removeEventListener('click', this.open); + } + /** * Update the count of items in the menu. * This is done by counting the number of elements with the data attribute @@ -107,7 +136,33 @@ export class DrilldownController extends Controller { } } + /** + * Prevent clicks on the w-action outlets from closing the dropdown. + * Usage: data-action="w-dropdown:clickaway->w-drilldown#preventOutletClickaway" + */ + preventOutletClickaway(e: Event) { + const clickawayEvent = e as CustomEvent<{ target: HTMLElement }>; + const target = clickawayEvent.detail.target; + if (!target) return; + const controlledIds = this.toggleTargets.map((toggle) => + toggle.getAttribute('aria-controls'), + ); + const clickawayControl = + target.closest('button')?.getAttribute('aria-controls') || ''; + if (controlledIds.includes(clickawayControl)) { + e.preventDefault(); + } + } + toggle(expanded: boolean, toggle: HTMLButtonElement) { + // If we're expanding, the toggle may be inside the w-dropdown outlet while + // the dropdown is hidden (e.g. opening directly to a submenu from an + // overall collapsed state). + // Ensure that the dropdown is shown so Tippy renders the toggle in the DOM. + if (this.hasWDropdownOutlet && expanded) { + this.wDropdownOutlet.show(); + } + const controls = toggle.getAttribute('aria-controls'); const content = this.element.querySelector(`#${controls}`); if (!content) { diff --git a/wagtail/admin/templates/wagtailadmin/shared/active_filters.html b/wagtail/admin/templates/wagtailadmin/shared/active_filters.html index 6d79509bfc..ba665da817 100644 --- a/wagtail/admin/templates/wagtailadmin/shared/active_filters.html +++ b/wagtail/admin/templates/wagtailadmin/shared/active_filters.html @@ -1,11 +1,11 @@ {% load i18n wagtailadmin_tags %}
    {% for filter in active_filters %} -
  • -
    +
  • +