Skip to content

Commit 80a02ae

Browse files
Fix: Trigger loading states for actions called via Unicorn.call()
1 parent 040e301 commit 80a02ae

File tree

4 files changed

+100
-4
lines changed

4 files changed

+100
-4
lines changed

src/django_unicorn/static/unicorn/js/eventListeners.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@ import { Element } from "./element.js";
44
/**
55
* Handles loading elements in the component.
66
* @param {Component} component Component.
7-
* @param {Element} targetElement Targetted element.
7+
* @param {Element} targetElement Targetted element. Can be null when called via Unicorn.call().
88
*/
9-
function handleLoading(component, targetElement) {
10-
targetElement.handleLoading();
9+
export function handleLoading(component, targetElement) {
10+
if (targetElement) {
11+
targetElement.handleLoading();
12+
}
1113

1214
// Look at all elements with a loading attribute
1315
component.loadingEls.forEach((loadingElement) => {
1416
if (loadingElement.target) {
17+
// Targeted loading elements only apply when there is a specific triggering element
18+
if (!targetElement) {
19+
return;
20+
}
21+
1522
// Get all ID matches
1623
if (loadingElement.target.includes("*")) {
1724
const targetRegex = toRegExp(loadingElement.target);

src/django_unicorn/static/unicorn/js/unicorn.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Component } from "./component.js";
22
import { isEmpty, hasValue } from "./utils.js";
33
import { components, lifecycleEvents } from "./store.js";
44
import { getMorpher } from "./morpher.js";
5+
import { handleLoading } from "./eventListeners.js";
56

67
let messageUrl = "";
78
let csrfTokenHeaderName = "X-CSRFToken";
@@ -186,6 +187,7 @@ export function call(componentNameOrKey, methodName, ...args) {
186187
methodName = `${methodName}(${argString})`;
187188
}
188189

190+
handleLoading(component, null);
189191
component.callMethod(methodName, 0, null, (err) => {
190192
console.error(err);
191193
});

src/django_unicorn/static/unicorn/js/unicorn.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/js/unicorn/call.test.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,93 @@ import { call } from "../../../src/django_unicorn/static/unicorn/js/unicorn.js";
33
import { components } from "../../../src/django_unicorn/static/unicorn/js/store.js";
44
import { getComponent } from "../utils.js";
55

6+
test("call triggers loading show", (t) => {
7+
const html = `
8+
<div unicorn:id="5jypjiyb" unicorn:name="text-inputs" unicorn:checksum="GXzew3Km">
9+
<div u:loading>Loading</div>
10+
</div>`;
11+
const component = getComponent(html);
12+
components[component.id] = component;
13+
component.callMethod = () => {};
14+
15+
t.is(component.loadingEls.length, 1);
16+
const loadingEl = component.loadingEls[0];
17+
t.true(loadingEl.el.hidden);
18+
19+
call("text-inputs", "testMethod");
20+
t.false(loadingEl.el.hidden);
21+
});
22+
23+
test("call triggers loading hide", (t) => {
24+
const html = `
25+
<div unicorn:id="5jypjiyb" unicorn:name="text-inputs" unicorn:checksum="GXzew3Km">
26+
<div u:loading.remove>Loading</div>
27+
</div>`;
28+
const component = getComponent(html);
29+
components[component.id] = component;
30+
component.callMethod = () => {};
31+
32+
t.is(component.loadingEls.length, 1);
33+
const loadingEl = component.loadingEls[0];
34+
t.false(loadingEl.el.hidden);
35+
36+
call("text-inputs", "testMethod");
37+
t.true(loadingEl.el.hidden);
38+
});
39+
40+
test("call triggers loading class", (t) => {
41+
const html = `
42+
<div unicorn:id="5jypjiyb" unicorn:name="text-inputs" unicorn:checksum="GXzew3Km">
43+
<div u:loading.class="loading">Loading</div>
44+
</div>`;
45+
const component = getComponent(html);
46+
components[component.id] = component;
47+
component.callMethod = () => {};
48+
49+
t.is(component.loadingEls.length, 1);
50+
const loadingEl = component.loadingEls[0];
51+
t.is(loadingEl.el.classList.length, 0);
52+
53+
call("text-inputs", "testMethod");
54+
t.is(loadingEl.el.classList.length, 1);
55+
t.is(loadingEl.el.classList[0], "loading");
56+
});
57+
58+
test("call triggers loading attr", (t) => {
59+
const html = `
60+
<div unicorn:id="5jypjiyb" unicorn:name="text-inputs" unicorn:checksum="GXzew3Km">
61+
<button u:loading.attr="disabled">Submit</button>
62+
</div>`;
63+
const component = getComponent(html);
64+
components[component.id] = component;
65+
component.callMethod = () => {};
66+
67+
t.is(component.loadingEls.length, 1);
68+
const loadingEl = component.loadingEls[0];
69+
t.true(typeof loadingEl.el.attributes.disabled === "undefined");
70+
71+
call("text-inputs", "testMethod");
72+
t.false(typeof loadingEl.el.attributes.disabled === "undefined");
73+
});
74+
75+
test("call does not trigger targeted loading element", (t) => {
76+
const html = `
77+
<div unicorn:id="5jypjiyb" unicorn:name="text-inputs" unicorn:checksum="GXzew3Km">
78+
<button id="myBtn" unicorn:click="test()">Click</button>
79+
<div u:loading u:target="myBtn">Loading</div>
80+
</div>`;
81+
const component = getComponent(html);
82+
components[component.id] = component;
83+
component.callMethod = () => {};
84+
85+
t.is(component.loadingEls.length, 1);
86+
const loadingEl = component.loadingEls[0];
87+
t.true(loadingEl.el.hidden);
88+
89+
call("text-inputs", "testMethod");
90+
t.true(loadingEl.el.hidden);
91+
});
92+
693
test("call a method", (t) => {
794
const component = getComponent();
895
components[component.id] = component;

0 commit comments

Comments
 (0)