From fae6ffcf062ed20900dc840ec23acaa568506a8e Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Fri, 13 Dec 2024 11:32:46 +1000 Subject: [PATCH] UX: Introduce 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. --- .../discourse/app/components/d-stat-tiles.gjs | 33 +++++++++ .../components/d-stat-tiles-test.gjs | 68 +++++++++++++++++++ .../stylesheets/common/components/_index.scss | 1 + .../common/components/d-stat-tiles.scss | 26 +++++++ 4 files changed, 128 insertions(+) create mode 100644 app/assets/javascripts/discourse/app/components/d-stat-tiles.gjs create mode 100644 app/assets/javascripts/discourse/tests/integration/components/d-stat-tiles-test.gjs create mode 100644 app/assets/stylesheets/common/components/d-stat-tiles.scss diff --git a/app/assets/javascripts/discourse/app/components/d-stat-tiles.gjs b/app/assets/javascripts/discourse/app/components/d-stat-tiles.gjs new file mode 100644 index 00000000000..2ec0d7b04ae --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/d-stat-tiles.gjs @@ -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 = ; + +const DStatTiles = ; + +export default DStatTiles; diff --git a/app/assets/javascripts/discourse/tests/integration/components/d-stat-tiles-test.gjs b/app/assets/javascripts/discourse/tests/integration/components/d-stat-tiles-test.gjs new file mode 100644 index 00000000000..cbb9e8ea3cc --- /dev/null +++ b/app/assets/javascripts/discourse/tests/integration/components/d-stat-tiles-test.gjs @@ -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(); + + 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(); + + 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(); + + 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(); + + assert + .dom(".d-stat-tiles .d-stat-tile a.d-stat-tile__value") + .hasAttribute("href", "https://meta.discourse.org"); + }); +}); diff --git a/app/assets/stylesheets/common/components/_index.scss b/app/assets/stylesheets/common/components/_index.scss index f242766ca1e..ffa69feec92 100644 --- a/app/assets/stylesheets/common/components/_index.scss +++ b/app/assets/stylesheets/common/components/_index.scss @@ -1,6 +1,7 @@ @import "badges"; @import "banner"; @import "d-breadcrumbs"; +@import "d-stat-tiles"; @import "bookmark-list"; @import "bookmark-modal"; @import "bookmark-menu"; diff --git a/app/assets/stylesheets/common/components/d-stat-tiles.scss b/app/assets/stylesheets/common/components/d-stat-tiles.scss new file mode 100644 index 00000000000..560e52d42c6 --- /dev/null +++ b/app/assets/stylesheets/common/components/d-stat-tiles.scss @@ -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; + } + } +}