diff --git a/addons/rose/addon/components/rose/button.js b/addons/rose/addon/components/rose/button.js index 3b11f0a24..02b50d397 100644 --- a/addons/rose/addon/components/rose/button.js +++ b/addons/rose/addon/components/rose/button.js @@ -10,4 +10,6 @@ import layout from '../../templates/components/rose/button'; */ export default Component.extend({ layout, + + tagName: '', }); diff --git a/addons/rose/addon/styles/rose/_icon.scss b/addons/rose/addon/styles/rose/_icon.scss index 003d287c0..08d4c6438 100644 --- a/addons/rose/addon/styles/rose/_icon.scss +++ b/addons/rose/addon/styles/rose/_icon.scss @@ -14,6 +14,6 @@ > svg { width: var(--size); height: var(--size); - vertical-align: middle; + vertical-align: text-top; } } diff --git a/addons/rose/addon/styles/rose/utilities/_button.scss b/addons/rose/addon/styles/rose/utilities/_button.scss index 29cee9130..3bb506024 100644 --- a/addons/rose/addon/styles/rose/utilities/_button.scss +++ b/addons/rose/addon/styles/rose/utilities/_button.scss @@ -9,6 +9,29 @@ padding: sizing.rems(off-xs) sizing.rems(l); border-radius: sizing.rems(xxxs); border: sizing.rems(xxxxs) solid; + + &.has-icon-left > .rose-icon:first-child { + margin-left: -(sizing.rems(xs)); + margin-right: sizing.rems(xxs); + } + + &.has-icon-right > .rose-icon:last-child { + margin-left: sizing.rems(xxs); + margin-right: -(sizing.rems(xs)); + } + + &.has-icon-only { + padding-right: sizing.rems(s); + padding-left: sizing.rems(s); + .rose-button-body { + position: absolute; + top: -1000px; + left: -1000px; + width: 1px; + height: 1px; + opacity: 0; + } + } } @mixin button-style($name, $default, $hover: &, $active: &, $disabled: &) { diff --git a/addons/rose/addon/templates/components/rose/button.hbs b/addons/rose/addon/templates/components/rose/button.hbs index 6256543d0..f109c1407 100644 --- a/addons/rose/addon/templates/components/rose/button.hbs +++ b/addons/rose/addon/templates/components/rose/button.hbs @@ -2,7 +2,32 @@ ...attributes type={{if @submit "submit" "button"}} disabled={{if @disabled "disabled"}} - class={{if @style (concat "rose-button-" @style)}} + class="{{if @style (concat "rose-button-" @style)}} {{if @iconLeft "has-icon-left"}} {{if @iconRight "has-icon-right"}} {{if @iconOnly "has-icon-only"}}" > - {{yield}} + + {{#if @iconLeft}} + + {{/if}} + + + {{yield}} + + + {{#if @iconOnly}} + + {{/if}} + + {{#if @iconRight}} + + {{/if}} + diff --git a/addons/rose/addon/templates/components/rose/icon.hbs b/addons/rose/addon/templates/components/rose/icon.hbs index d215526f6..8973d5a78 100644 --- a/addons/rose/addon/templates/components/rose/icon.hbs +++ b/addons/rose/addon/templates/components/rose/icon.hbs @@ -1,3 +1,3 @@ - + {{inline-svg @name}} diff --git a/addons/rose/stories/button.stories.js b/addons/rose/stories/button.stories.js index 645f8c39c..53cca2788 100644 --- a/addons/rose/stories/button.stories.js +++ b/addons/rose/stories/button.stories.js @@ -6,19 +6,29 @@ export default { title: 'Rose::Button', }; +const icons = { + none: null, + 'alert-circle-fill': 'alert-circle-fill', + 'chevron-right': 'chevron-right', + 'copy-action': 'copy-action', + 'lock-closed': 'lock-closed', +}; + export const Normal = () => ({ template: hbs` {{text}} `, context: { - text: text('text', 'Button 🌈'), + text: text('text', 'Button'), submit: boolean('submit', false), disabled: boolean('disabled', false), style: select( @@ -32,6 +42,27 @@ export const Normal = () => ({ }, 'primary' ), + iconLeft: select('iconLeft', icons, 'copy-action'), + iconRight: select('iconRight', icons, 'chevron-right'), onClick: action('clicked'), }, }); + +export const IconOnly = () => ({ + template: hbs` +

Icon-only buttons visually hide their text, revealing just an icon. + It's still important to include meaningful text in the body of the + button for accessibility purposes.

+ + + {{text}} + + `, + context: { + text: text('text', 'Button'), + iconOnly: select('iconOnly', icons, 'lock-closed'), + }, +}); diff --git a/addons/rose/tests/integration/components/rose/button-test.js b/addons/rose/tests/integration/components/rose/button-test.js index 89ba514fb..be532a65f 100644 --- a/addons/rose/tests/integration/components/rose/button-test.js +++ b/addons/rose/tests/integration/components/rose/button-test.js @@ -13,23 +13,35 @@ module('Integration | Component | rose/button', function (hooks) { test('it is type="button" by default', async function (assert) { await render(hbs`Button`); - assert.equal(await find('button').type, 'button'); + assert.equal(find('button').type, 'button'); }); test('it is type="submit" when @submit={{true}}', async function (assert) { await render(hbs``); - assert.equal(await find('button').type, 'submit'); + assert.equal(find('button').type, 'submit'); }); test('it is disabled when @disabled={{true}}', async function (assert) { await render(hbs``); - assert.equal(await find('button').disabled, true); + assert.equal(find('button').disabled, true); }); test('it adds a style class', async function (assert) { await render(hbs``); - assert.ok(await find('button.rose-button-primary')); + assert.ok(find('.rose-button-primary')); await render(hbs``); - assert.ok(await find('button.rose-button-secondary')); + assert.ok(find('.rose-button-secondary')); + }); + + test('it supports left and right icons', async function (assert) { + await render(hbs``); + assert.ok(find('.has-icon-left .rose-icon')); + await render(hbs``); + assert.ok(find('.has-icon-right .rose-icon')); + }); + + test('it supports an icon-only type', async function (assert) { + await render(hbs``); + assert.ok(find('.has-icon-only .rose-icon')); }); });