From 286b4e535e1abb0024170a205decba4f9f82d3c4 Mon Sep 17 00:00:00 2001 From: Joffrey JAFFEUX Date: Mon, 11 May 2020 22:09:44 +0200 Subject: [PATCH] DEV: allows buttons to define aria-label (#9747) --- .../discourse/app/components/d-button.js | 51 ++++++------- .../app/lib/register-topic-footer-button.js | 13 ++++ .../app/templates/components/d-button.hbs | 4 +- .../components/topic-footer-buttons.hbs | 1 + test/javascripts/components/d-button-test.js | 72 +++++++++++++++++++ 5 files changed, 114 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/d-button.js b/app/assets/javascripts/discourse/app/components/d-button.js index 4df816a3474..ad4d32a5a24 100644 --- a/app/assets/javascripts/discourse/app/components/d-button.js +++ b/app/assets/javascripts/discourse/app/components/d-button.js @@ -5,12 +5,17 @@ import discourseComputed from "discourse-common/utils/decorators"; import DiscourseURL from "discourse/lib/url"; export default Component.extend({ + tagName: "button", // subclasses need this layoutName: "components/d-button", - form: null, - type: "button", + title: null, + translatedTitle: null, + label: null, + translatedLabel: null, + ariaLabel: null, + translatedAriaLabel: null, isLoading: computed({ set(key, value) { @@ -19,7 +24,6 @@ export default Component.extend({ } }), - tagName: "button", classNameBindings: [ "isLoading:is-loading", "btnLink::btn", @@ -30,8 +34,8 @@ export default Component.extend({ attributeBindings: [ "form", "isDisabled:disabled", - "translatedTitle:title", - "translatedLabel:aria-label", + "computedTitle:title", + "computedAriaLabel:aria-label", "tabindex", "type" ], @@ -46,7 +50,7 @@ export default Component.extend({ btnLink: equal("display", "link"), - @discourseComputed("icon", "translatedLabel") + @discourseComputed("icon", "computedLabel") btnType(icon, translatedLabel) { if (icon) { return translatedLabel ? "btn-icon-text" : "btn-icon"; @@ -55,28 +59,25 @@ export default Component.extend({ } }, - noText: empty("translatedLabel"), + noText: empty("computedLabel"), - @discourseComputed("title") - translatedTitle: { - get() { - if (this._translatedTitle) return this._translatedTitle; - if (this.title) return I18n.t(this.title); - }, - set(value) { - return (this._translatedTitle = value); - } + @discourseComputed("title", "translatedTitle") + computedTitle(title, translatedTitle) { + if (this.title) return I18n.t(title); + return translatedTitle; }, - @discourseComputed("label") - translatedLabel: { - get() { - if (this._translatedLabel) return this._translatedLabel; - if (this.label) return I18n.t(this.label); - }, - set(value) { - return (this._translatedLabel = value); - } + @discourseComputed("label", "translatedLabel") + computedLabel(label, translatedLabel) { + if (this.label) return I18n.t(label); + return translatedLabel; + }, + + @discourseComputed("ariaLabel", "translatedAriaLabel", "computedLabel") + computedAriaLabel(ariaLabel, translatedAriaLabel, computedLabel) { + if (ariaLabel) return I18n.t(ariaLabel); + if (translatedAriaLabel) return translatedAriaLabel; + return computedLabel; }, click() { diff --git a/app/assets/javascripts/discourse/app/lib/register-topic-footer-button.js b/app/assets/javascripts/discourse/app/lib/register-topic-footer-button.js index c2dd9eb96cf..ecac09a89a2 100644 --- a/app/assets/javascripts/discourse/app/lib/register-topic-footer-button.js +++ b/app/assets/javascripts/discourse/app/lib/register-topic-footer-button.js @@ -28,6 +28,10 @@ export function registerTopicFooterButton(button) { label: null, translatedLabel: null, + // local key path for aria label + ariaLabel: null, + translatedAriaLabel: null, + // is this button disaplyed in the mobile dropdown or as an inline button ? dropdown: false, @@ -98,6 +102,15 @@ export function getTopicFooterButtons() { ? I18n.t(label) : _compute(button, "translatedLabel"); + const ariaLabel = _compute(button, "ariaLabel"); + if (ariaLabel) { + discourseComputedButon.ariaLabel = I18n.t(ariaLabel); + } else { + const translatedAriaLabel = _compute(button, "translatedAriaLabel"); + discourseComputedButon.ariaLabel = + translatedAriaLabel || discourseComputedButon.label; + } + const title = _compute(button, "title"); discourseComputedButon.title = title ? I18n.t(title) diff --git a/app/assets/javascripts/discourse/app/templates/components/d-button.hbs b/app/assets/javascripts/discourse/app/templates/components/d-button.hbs index 6c9ed400751..10d12b4d836 100644 --- a/app/assets/javascripts/discourse/app/templates/components/d-button.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/d-button.hbs @@ -10,8 +10,8 @@ {{/if}} {{/if}} -{{#if translatedLabel}} - {{html-safe translatedLabel}}{{#if ellipsis}}…{{/if}} +{{#if computedLabel}} + {{html-safe computedLabel}}{{#if ellipsis}}…{{/if}} {{/if}} {{yield}} diff --git a/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs b/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs index 766ae9bf2b0..007ec9c19d2 100644 --- a/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs @@ -29,6 +29,7 @@ icon=button.icon translatedLabel=button.label translatedTitle=button.title + translatedAriaLabel=button.ariaLabel disabled=button.disabled}} {{/each}} diff --git a/test/javascripts/components/d-button-test.js b/test/javascripts/components/d-button-test.js index 5b0bdeb150b..1d85d922c4e 100644 --- a/test/javascripts/components/d-button-test.js +++ b/test/javascripts/components/d-button-test.js @@ -100,3 +100,75 @@ componentTest("disabled button", { assert.ok(find("button:not([disabled])").length, "the button is enabled"); } }); + +componentTest("aria-label", { + template: + "{{d-button ariaLabel=ariaLabel translatedAriaLabel=translatedAriaLabel}}", + + beforeEach() { + I18n.translations[I18n.locale].js.test = { fooAriaLabel: "foo" }; + }, + + test(assert) { + this.set("ariaLabel", "test.fooAriaLabel"); + + assert.equal( + find("button")[0].getAttribute("aria-label"), + I18n.t("test.fooAriaLabel") + ); + + this.setProperties({ + ariaLabel: null, + translatedAriaLabel: "bar" + }); + + assert.equal(find("button")[0].getAttribute("aria-label"), "bar"); + } +}); + +componentTest("title", { + template: "{{d-button title=title translatedTitle=translatedTitle}}", + + beforeEach() { + I18n.translations[I18n.locale].js.test = { fooTitle: "foo" }; + }, + + test(assert) { + this.set("title", "test.fooTitle"); + assert.equal( + find("button")[0].getAttribute("title"), + I18n.t("test.fooTitle") + ); + + this.setProperties({ + title: null, + translatedTitle: "bar" + }); + + assert.equal(find("button")[0].getAttribute("title"), "bar"); + } +}); + +componentTest("label", { + template: "{{d-button label=label translatedLabel=translatedLabel}}", + + beforeEach() { + I18n.translations[I18n.locale].js.test = { fooLabel: "foo" }; + }, + + test(assert) { + this.set("label", "test.fooLabel"); + + assert.equal( + find("button .d-button-label").text(), + I18n.t("test.fooLabel") + ); + + this.setProperties({ + label: null, + translatedLabel: "bar" + }); + + assert.equal(find("button .d-button-label").text(), "bar"); + } +});