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).
This commit is contained in:
Dan Gebhardt 2022-10-27 15:12:34 -04:00 committed by GitHub
parent 9b1536fb83
commit 952b033165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 113 additions and 1 deletions

View File

@ -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."

View File

@ -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`
<a
href
class="btn btn-default no-text mobile-gif-insert"
aria-label={{i18n "gif.composer_title"}}
{{action "doSomething"}}
>
{{d-icon "discourse-gifs-gif"}}
</a>
`;
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`
<ExampleClassicButton @onDoSomething={{this.onOneClick}} />
`);
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`
<ExampleClassicButtonWithActions @onDoSomething={{this.onOneClick}} />
`);
assert.strictEqual(this.oneClicked, undefined);
await click("a.btn");
assert.strictEqual(this.oneClicked, 0);
});
});
});