From 952b033165c2b77872d8cc79b0678dc4293c5ee1 Mon Sep 17 00:00:00 2001 From: Dan Gebhardt Date: Thu, 27 Oct 2022 15:12:34 -0400 Subject: [PATCH] FIX: Ensure that custom {{action}} modifier works with actions hash (#18779) A callback that's provided as a string, such as `{{action "doSomething"}}`, may target the method `doSomething` on the context OR the context's `action` hash (if it exists). --- .../app/lib/ember-action-modifier.js | 7 +- .../unit/lib/ember-action-modifer-test.js | 107 ++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/app/lib/ember-action-modifier.js b/app/assets/javascripts/discourse/app/lib/ember-action-modifier.js index 61177cce935..2d3059befb8 100644 --- a/app/assets/javascripts/discourse/app/lib/ember-action-modifier.js +++ b/app/assets/javascripts/discourse/app/lib/ember-action-modifier.js @@ -16,7 +16,12 @@ export const actionModifier = modifier( { on, bubbles, preventDefault, allowedKeys } ) => { const handler = (event) => { - const fn = typeof callback === "string" ? context[callback] : callback; + let fn; + if (typeof callback === "string") { + fn = context.actions?.[callback] ?? context[callback]; + } else if (typeof callback === "function") { + fn = callback; + } if (fn === undefined) { throw new Error( "Unexpected callback for `action` modifier. Please provide either a function or the name of a method on the current context." diff --git a/app/assets/javascripts/discourse/tests/unit/lib/ember-action-modifer-test.js b/app/assets/javascripts/discourse/tests/unit/lib/ember-action-modifer-test.js index 8ede3ff97b6..6a865100e1a 100644 --- a/app/assets/javascripts/discourse/tests/unit/lib/ember-action-modifer-test.js +++ b/app/assets/javascripts/discourse/tests/unit/lib/ember-action-modifer-test.js @@ -2,6 +2,7 @@ import { module, test } from "qunit"; import { setupRenderingTest } from "ember-qunit"; import { click, doubleClick, render } from "@ember/test-helpers"; import { hbs } from "ember-cli-htmlbars"; +import { default as ClassicComponent } from "@ember/component"; module("Unit | Lib | ember-action-modifer", function (hooks) { setupRenderingTest(hooks); @@ -73,4 +74,110 @@ module("Unit | Lib | ember-action-modifer", function (hooks) { assert.strictEqual(this.dblClicked, 0); }); + + module("used on a classic component", function () { + const ExampleClassicButton = ClassicComponent.extend({ + tagName: "", + onDoSomething: null, + + doSomething() { + this.onDoSomething?.("doSomething"); + }, + }); + const ExampleClassicButtonWithActions = ClassicComponent.extend({ + tagName: "", + onDoSomething: null, + + doSomething() { + this.onDoSomething?.("doSomething"); + }, + + actions: { + doSomething() { + this.onDoSomething?.("actions.doSomething"); + }, + }, + }); + const exampleClassicButtonTemplate = hbs` + + {{d-icon "discourse-gifs-gif"}} + + `; + + hooks.beforeEach(function () { + this.owner.register( + "component:example-classic-button", + ExampleClassicButton + ); + this.owner.register( + "template:components/example-classic-button", + exampleClassicButtonTemplate + ); + this.owner.register( + "component:example-classic-button-with-actions", + ExampleClassicButtonWithActions + ); + this.owner.register( + "template:components/example-classic-button-with-actions", + exampleClassicButtonTemplate + ); + }); + + test("it can target a listener on the context", async function (assert) { + let i = 0; + + this.setProperties({ + onOneClick: (source) => { + assert.strictEqual( + source, + "doSomething", + "handler on context is invoked" + ); + this.set("oneClicked", i++); + }, + oneClicked: undefined, + }); + + await render(hbs` + + `); + + assert.strictEqual(this.oneClicked, undefined); + + await click("a.btn"); + + assert.strictEqual(this.oneClicked, 0); + }); + + test("it can target a listener on the actions hash", async function (assert) { + let i = 0; + + this.setProperties({ + onOneClick: (source) => { + assert.strictEqual( + source, + "actions.doSomething", + "handler on actions hash is given precedence" + ); + this.set("oneClicked", i++); + }, + oneClicked: undefined, + }); + + await render(hbs` + + `); + + assert.strictEqual(this.oneClicked, undefined); + + await click("a.btn"); + + assert.strictEqual(this.oneClicked, 0); + }); + }); });