mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: adds a loading property to d-button (#9072)
Usage:
```
{{d-button icon="times" label="foo.bar" isLoading=true}}
```
Note that a button loading without an icon will shrink text size to prevent button to jump in size.
A button while loading is disabled.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { notEmpty, empty, equal } from "@ember/object/computed";
|
import { notEmpty, empty, equal } from "@ember/object/computed";
|
||||||
|
import { computed } from "@ember/object";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
@@ -11,17 +12,36 @@ export default Component.extend({
|
|||||||
|
|
||||||
type: "button",
|
type: "button",
|
||||||
|
|
||||||
|
isLoading: computed({
|
||||||
|
set(key, value) {
|
||||||
|
this.set("forceDisabled", value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
tagName: "button",
|
tagName: "button",
|
||||||
classNameBindings: ["btnLink::btn", "btnLink", "noText", "btnType"],
|
classNameBindings: [
|
||||||
|
"isLoading:is-loading",
|
||||||
|
"btnLink::btn",
|
||||||
|
"btnLink",
|
||||||
|
"noText",
|
||||||
|
"btnType"
|
||||||
|
],
|
||||||
attributeBindings: [
|
attributeBindings: [
|
||||||
"form",
|
"form",
|
||||||
"disabled",
|
"isDisabled:disabled",
|
||||||
"translatedTitle:title",
|
"translatedTitle:title",
|
||||||
"translatedLabel:aria-label",
|
"translatedLabel:aria-label",
|
||||||
"tabindex",
|
"tabindex",
|
||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
isDisabled: computed("disabled", "forceDisabled", function() {
|
||||||
|
return this.forceDisabled || this.disabled;
|
||||||
|
}),
|
||||||
|
|
||||||
|
forceDisabled: false,
|
||||||
|
|
||||||
btnIcon: notEmpty("icon"),
|
btnIcon: notEmpty("icon"),
|
||||||
|
|
||||||
btnLink: equal("display", "link"),
|
btnLink: equal("display", "link"),
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
{{#if icon}}
|
{{#if icon}}
|
||||||
{{d-icon icon}}
|
{{#if isLoading}}
|
||||||
|
{{d-icon "spinner" class="loading-icon"}}
|
||||||
|
{{else}}
|
||||||
|
{{d-icon icon}}
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if isLoading}}
|
||||||
|
{{d-icon "spinner" class="loading-icon"}}
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if translatedLabel}}
|
{{#if translatedLabel}}
|
||||||
|
|||||||
@@ -67,6 +67,31 @@
|
|||||||
}
|
}
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: none;
|
||||||
|
margin: 0 6.75px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-loading {
|
||||||
|
&.btn-text {
|
||||||
|
.d-button-label {
|
||||||
|
font-size: $font-down-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-small {
|
||||||
|
.loading-icon {
|
||||||
|
font-size: $font-down-1;
|
||||||
|
margin-right: 0.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
-webkit-animation: rotate-forever 1s infinite linear, fadein 1s;
|
||||||
|
animation: rotate-forever 1s infinite linear, fadein 1s;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.hidden {
|
.btn.hidden {
|
||||||
|
|||||||
@@ -198,7 +198,8 @@ module SvgSprite
|
|||||||
"user-shield",
|
"user-shield",
|
||||||
"user-times",
|
"user-times",
|
||||||
"users",
|
"users",
|
||||||
"wrench"
|
"wrench",
|
||||||
|
"spinner"
|
||||||
])
|
])
|
||||||
|
|
||||||
FA_ICON_MAP = { 'far fa-' => 'far-', 'fab fa-' => 'fab-', 'fas fa-' => '', 'fa-' => '' }
|
FA_ICON_MAP = { 'far fa-' => 'far-', 'fab fa-' => 'fab-', 'fas fa-' => '', 'fa-' => '' }
|
||||||
|
|||||||
@@ -54,3 +54,49 @@ componentTest("link-styled button", {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
componentTest("isLoading button", {
|
||||||
|
template: "{{d-button isLoading=isLoading}}",
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("isLoading", true);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.ok(
|
||||||
|
find("button.is-loading .spinner").length,
|
||||||
|
"it has a spinner showing"
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
find("button[disabled]").length,
|
||||||
|
"while loading the button is disabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.set("isLoading", false);
|
||||||
|
|
||||||
|
assert.notOk(
|
||||||
|
find("button .spinner").length,
|
||||||
|
"it doesn't have a spinner showing"
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
find("button:not([disabled])").length,
|
||||||
|
"while not loading the button is enabled"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest("disabled button", {
|
||||||
|
template: "{{d-button disabled=disabled}}",
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("disabled", true);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.ok(find("button[disabled]").length, "the button is disabled");
|
||||||
|
|
||||||
|
this.set("disabled", false);
|
||||||
|
|
||||||
|
assert.ok(find("button:not([disabled])").length, "the button is enabled");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user