Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/reference/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

<meta data-controller="callout" data-callout-text-value="resize@window">

Expand Down Expand Up @@ -215,12 +215,12 @@ 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, including with the `params` action parameters on the submitter element |
| element | [Element]: The element where the action descriptor is declared |

[toggle]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLDetailsElement/toggle_event
[Event]: https://developer.mozilla.org/en-US/docs/web/api/event
Expand Down
15 changes: 9 additions & 6 deletions src/core/binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ export class Binding {
}

handleEvent(event: Event) {
if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(event)) {
this.invokeWithEvent(event)
const actionEvent = this.prepareActionEvent(event)
if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(actionEvent)) {
this.invokeWithEvent(actionEvent)
}
}

Expand Down Expand Up @@ -65,12 +66,14 @@ export class Binding {
return passes
}

private invokeWithEvent(event: Event) {
private prepareActionEvent(event: Event): ActionEvent {
return Object.assign(event, { params: this.action.params })
}

private invokeWithEvent(event: ActionEvent) {
const { target, currentTarget } = event
try {
const { params } = this.action
const actionEvent: ActionEvent = Object.assign(event, { params })
this.method.call(this.controller, actionEvent)
this.method.call(this.controller, event)
this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName })
} catch (error: any) {
const { identifier, controller, element, index } = this
Expand Down
37 changes: 37 additions & 0 deletions src/tests/modules/core/event_options_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,43 @@ export default class EventOptionsTests extends LogControllerTestCase {
this.assertActions({ name: "log", eventType: "toggle" })
}

async "test custom action option callback event contains params"() {
let lastActionEventParams: Object = {}

// clone the params to ensure we check the value as the callback receives it
// not the event after all actions have resolved

const mockCallback = ({ event: { params = {} } = {} }) => {
lastActionEventParams = { ...params }
}

this.application.registerActionOption("all", (options: Object) => {
mockCallback(options)
return true
})

this.buttonElement.setAttribute("data-c-custom-number-param", "41")
this.buttonElement.setAttribute("data-c-custom-string-param", "validation")
this.buttonElement.setAttribute("data-c-custom-boolean-param", "true")
this.buttonElement.setAttribute("data-d-should-ignore-param", "_IGNORED_")

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 })

const expectedEventParams = {
customBoolean: true,
customNumber: 41,
customString: "validation",
}

this.assert.deepEqual(this.controllerConstructor.actionLog[0].params, expectedEventParams)

this.assert.deepEqual(lastActionEventParams, expectedEventParams)
}

setAction(element: Element, value: string) {
element.setAttribute("data-action", value)
return this.nextFrame
Expand Down