From 58174492f337ac8647f12c5f62b313d942d51c19 Mon Sep 17 00:00:00 2001 From: "LB (Ben Johnston)" Date: Wed, 21 Jun 2023 17:58:03 +1000 Subject: [PATCH] Pass controller instance to registerActionOption callback - Add ability for registerActionOption callbacks to receive the controller instance - Relates to #668 --- docs/reference/actions.md | 15 +++---- src/core/action_descriptor.ts | 3 ++ src/core/binding.ts | 3 +- src/tests/modules/core/event_options_tests.ts | 42 +++++++++++++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/docs/reference/actions.md b/docs/reference/actions.md index fad03108..1a25433e 100644 --- a/docs/reference/actions.md +++ b/docs/reference/actions.md @@ -131,7 +131,7 @@ The list of supported modifier keys is shown below. Sometimes a controller needs to listen for events dispatched on the global `window` or `document` objects. -You can append `@window` or `@document` to the event name (along with any filter modifier) in an action descriptor to install the event listener on `window` or `document`, respectively, as in the following example: +You can append `@window` or `@document` to the event name (along with any filter modifer) in an action descriptor to install the event listener on `window` or `document`, respectively, as in the following example: @@ -215,12 +215,13 @@ route the event to the controller action, return `true`. The callback accepts a single object argument with the following keys: -Name | Description ---------|------------ -name | String: The option's name (`"open"` in the example above) -value | Boolean: The value of the option (`:open` would yield `true`, `:!open` would yield `false`) -event | [Event][]: The event instance -element | [Element]: The element where the action descriptor is declared +| Name | Description | +| ---------- | ------------------------------------------------------------------------------------------- | +| name | String: The option's name (`"open"` in the example above) | +| value | Boolean: The value of the option (`:open` would yield `true`, `:!open` would yield `false`) | +| event | [Event][]: The event instance | +| element | [Element]: The element where the action descriptor is declared | +| controller | The `Controller` instance which would receive the method call | [toggle]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLDetailsElement/toggle_event [Event]: https://developer.mozilla.org/en-US/docs/web/api/event diff --git a/src/core/action_descriptor.ts b/src/core/action_descriptor.ts index 917bf240..35a4a211 100644 --- a/src/core/action_descriptor.ts +++ b/src/core/action_descriptor.ts @@ -1,3 +1,5 @@ +import type { Controller } from "./controller" + export type ActionDescriptorFilters = Record export type ActionDescriptorFilter = (options: ActionDescriptorFilterOptions) => boolean type ActionDescriptorFilterOptions = { @@ -5,6 +7,7 @@ type ActionDescriptorFilterOptions = { value: boolean event: Event element: Element + controller: Controller } export const defaultActionDescriptorFilters: ActionDescriptorFilters = { diff --git a/src/core/binding.ts b/src/core/binding.ts index bbaea3db..522bb6a0 100644 --- a/src/core/binding.ts +++ b/src/core/binding.ts @@ -49,6 +49,7 @@ export class Binding { private applyEventModifiers(event: Event): boolean { const { element } = this.action const { actionDescriptorFilters } = this.context.application + const { controller } = this.context let passes = true @@ -56,7 +57,7 @@ export class Binding { if (name in actionDescriptorFilters) { const filter = actionDescriptorFilters[name] - passes = passes && filter({ name, value, event, element }) + passes = passes && filter({ name, value, event, element, controller }) } else { continue } diff --git a/src/tests/modules/core/event_options_tests.ts b/src/tests/modules/core/event_options_tests.ts index 403cb10a..be135614 100644 --- a/src/tests/modules/core/event_options_tests.ts +++ b/src/tests/modules/core/event_options_tests.ts @@ -1,3 +1,4 @@ +import type { Controller } from "src/core" import { LogControllerTestCase } from "../../cases/log_controller_test_case" export default class EventOptionsTests extends LogControllerTestCase { @@ -177,6 +178,47 @@ export default class EventOptionsTests extends LogControllerTestCase { this.assertNoActions() } + async "test custom action option callback params contain the controller instance"() { + let lastActionOptions: { controller?: Controller } = {} + + const mockCallback = (options: Object) => { + lastActionOptions = options + } + + this.application.registerActionOption("all", (options: Object) => { + mockCallback(options) + return true + }) + + await this.setAction(this.buttonElement, "click->c#log:all") + + await this.triggerEvent(this.buttonElement, "click") + + this.assertActions({ name: "log", identifier: "c", eventType: "click", currentTarget: this.buttonElement }) + + this.assert.deepEqual(["name", "value", "event", "element", "controller"], Object.keys(lastActionOptions)) + + this.assert.equal( + lastActionOptions.controller, + this.application.getControllerForElementAndIdentifier(this.element, "c") + ) + + this.controllerConstructor.actionLog = [] // clear actions + + await this.setAction(this.buttonElement, "click->d#log:all") + + await this.triggerEvent(this.buttonElement, "click") + + this.assertActions({ name: "log", identifier: "d", eventType: "click", currentTarget: this.buttonElement }) + + this.assert.deepEqual(["name", "value", "event", "element", "controller"], Object.keys(lastActionOptions)) + + this.assert.equal( + lastActionOptions.controller, + this.application.getControllerForElementAndIdentifier(this.element, "d") + ) + } + async "test custom option"() { this.application.registerActionOption("open", ({ value, event: { type, target } }) => { switch (type) {