mirror of
https://github.com/discourse/discourse.git
synced 2024-12-20 13:03:43 +08:00
UX: Introduce <DStatTiles /> component (#30238)
Introduces a new component used to show a grid of stats on any page, mostly used for dashboards and config pages. This component yields a hash with a `Tile` component property, and the caller can loop through their stats and display them using this component. Each stat needs a @label and a @value at minimum, but can also pass in a @tooltip and a @url.
This commit is contained in:
parent
54f0dd40ad
commit
fae6ffcf06
|
@ -0,0 +1,33 @@
|
||||||
|
import { hash } from "@ember/helper";
|
||||||
|
import { number } from "discourse/lib/formatter";
|
||||||
|
import DTooltip from "float-kit/components/d-tooltip";
|
||||||
|
|
||||||
|
const DStatTile = <template>
|
||||||
|
<div class="d-stat-tile" role="group">
|
||||||
|
<div class="d-stat-tile__top">
|
||||||
|
<span class="d-stat-tile__label">{{@label}}</span>
|
||||||
|
{{#if @tooltip}}
|
||||||
|
<DTooltip
|
||||||
|
class="d-stat-tile__tooltip"
|
||||||
|
@icon="circle-question"
|
||||||
|
@content={{@tooltip}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{#if @url}}
|
||||||
|
<a href={{@url}} class="d-stat-tile__value" title={{@value}}>
|
||||||
|
{{number @value}}
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<span class="d-stat-tile__value" title={{@value}}>{{number @value}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
const DStatTiles = <template>
|
||||||
|
<div class="d-stat-tiles" ...attributes>
|
||||||
|
{{yield (hash Tile=DStatTile)}}
|
||||||
|
</div>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default DStatTiles;
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { render, triggerEvent } from "@ember/test-helpers";
|
||||||
|
import { module, test } from "qunit";
|
||||||
|
import DStatTiles from "discourse/components/d-stat-tiles";
|
||||||
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
module("Integration | Component | DStatTiles", function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
test("formats the @value in a readable way with the raw number as the title attr", async function (assert) {
|
||||||
|
await render(<template>
|
||||||
|
<DStatTiles as |tiles|>
|
||||||
|
<tiles.Tile @value="12555999" @label={{i18n "bootstrap_mode"}} />
|
||||||
|
</DStatTiles>
|
||||||
|
</template>);
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom(".d-stat-tiles .d-stat-tile .d-stat-tile__value")
|
||||||
|
.hasText("12.6M");
|
||||||
|
assert
|
||||||
|
.dom(".d-stat-tiles .d-stat-tile .d-stat-tile__value")
|
||||||
|
.hasAttribute("title", "12555999");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders the @label", async function (assert) {
|
||||||
|
await render(<template>
|
||||||
|
<DStatTiles as |tiles|>
|
||||||
|
<tiles.Tile @value="12555999" @label={{i18n "bootstrap_mode"}} />
|
||||||
|
</DStatTiles>
|
||||||
|
</template>);
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom(".d-stat-tiles .d-stat-tile .d-stat-tile__label")
|
||||||
|
.hasText(i18n("bootstrap_mode"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders the optional @tooltip", async function (assert) {
|
||||||
|
await render(<template>
|
||||||
|
<DStatTiles as |tiles|>
|
||||||
|
<tiles.Tile
|
||||||
|
@value="12555999"
|
||||||
|
@label={{i18n "bootstrap_mode"}}
|
||||||
|
@tooltip={{i18n "bootstrap_mode"}}
|
||||||
|
/>
|
||||||
|
</DStatTiles>
|
||||||
|
</template>);
|
||||||
|
|
||||||
|
assert.dom(".d-stat-tile__tooltip").exists();
|
||||||
|
await triggerEvent(".fk-d-tooltip__trigger", "mousemove");
|
||||||
|
assert.dom(".fk-d-tooltip__content").hasText(i18n("bootstrap_mode"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("wraps the value in a link if @url is provided", async function (assert) {
|
||||||
|
await render(<template>
|
||||||
|
<DStatTiles as |tiles|>
|
||||||
|
<tiles.Tile
|
||||||
|
@value="12555999"
|
||||||
|
@label={{i18n "bootstrap_mode"}}
|
||||||
|
@url="https://meta.discourse.org"
|
||||||
|
/>
|
||||||
|
</DStatTiles>
|
||||||
|
</template>);
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom(".d-stat-tiles .d-stat-tile a.d-stat-tile__value")
|
||||||
|
.hasAttribute("href", "https://meta.discourse.org");
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,6 +1,7 @@
|
||||||
@import "badges";
|
@import "badges";
|
||||||
@import "banner";
|
@import "banner";
|
||||||
@import "d-breadcrumbs";
|
@import "d-breadcrumbs";
|
||||||
|
@import "d-stat-tiles";
|
||||||
@import "bookmark-list";
|
@import "bookmark-list";
|
||||||
@import "bookmark-modal";
|
@import "bookmark-modal";
|
||||||
@import "bookmark-menu";
|
@import "bookmark-menu";
|
||||||
|
|
26
app/assets/stylesheets/common/components/d-stat-tiles.scss
Normal file
26
app/assets/stylesheets/common/components/d-stat-tiles.scss
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
.d-stat-tiles {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 1em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
|
||||||
|
.d-stat-tile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1em;
|
||||||
|
background: var(--primary-very-low);
|
||||||
|
border-radius: 0.25em;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
font-size: 0.875em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
color: var(--primary);
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user