Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions addon/components/utils/account-banner.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,58 @@
{{/if}}

{{#if (or @subtitle (has-block "custom-subtitle"))}}
<div
class={{concat
"account-banner__selection fx-row fx-gap-px-6 fx-xalign-center "
(unless this.canSelectItem " account-banner__selection--unique-account")
}}
role="button"
data-control-name="account-banner-selection-subtitle"
{{on "click" this.toggleSelectionDropdown}}
{{on-click-outside this.closeSelectionDropdown}}
>
{{#if @subtitle}}
<span
class="font-color-gray-500 text-ellipsis font-size-sm"
data-control-name="account-banner-selected-item-label"
>
{{@subtitle}}
</span>
{{else if (has-block "custom-subtitle")}}
{{yield to="custom-subtitle"}}
{{/if}}

{{#if this.canSelectItem}}
<OSS::Icon
@icon={{if this.displaySelectableItems "fa-chevron-up" "fa-chevron-down"}}
class="font-color-gray-500"
/>
{{#if @subtitle}}
<div class="fx-col fx-gap-px-3">
<div
class="upf-floating-menu upf-floating-menu--{{if this.displaySelectableItems 'visible' 'hidden'}}"
class={{concat
"account-banner__selection fx-row fx-gap-px-6 fx-xalign-center "
(unless this.canSelectItem " account-banner__selection--unique-account")
}}
role="button"
data-control-name="account-banner-selection-subtitle"
{{on "click" this.toggleSelectionDropdown}}
{{on-click-outside this.closeSelectionDropdown}}
>
{{#each @selectableItems as |item|}}
{{#if (has-block "selectable-item")}}
{{yield item this.closeSelectionDropdown to="selectable-item"}}
{{/if}}
{{/each}}
{{#if @required}}
<span
class="fx-row font-color-gray-500 text-ellipsis font-size-sm"
data-control-name="account-banner-selected-item-label"
{{required-input}}
>
{{@subtitle}}
</span>
{{else}}
<span
class="font-color-gray-500 text-ellipsis font-size-sm"
data-control-name="account-banner-selected-item-label"
>
{{@subtitle}}
</span>
{{/if}}

{{#if this.canSelectItem}}
<OSS::Icon
@icon={{if this.displaySelectableItems "fa-chevron-up" "fa-chevron-down"}}
class="font-color-gray-500"
/>
<div class="upf-floating-menu upf-floating-menu--{{if this.displaySelectableItems 'visible' 'hidden'}}">
{{#each @selectableItems as |item|}}
{{#if (has-block "selectable-item")}}
{{yield item this.closeSelectionDropdown to="selectable-item"}}
{{/if}}
{{/each}}
</div>
{{/if}}
</div>
{{/if}}
</div>
{{#if this.feedbackMessage}}
<span class={{concat "font-color-" this.feedbackMessage.type "-500"}}>
{{this.feedbackMessage.value}}
</span>
{{/if}}
</div>
{{else if (has-block "custom-subtitle")}}
{{yield to="custom-subtitle"}}
{{/if}}
{{/if}}
</div>

Expand Down
13 changes: 13 additions & 0 deletions addon/components/utils/account-banner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { isBlank } from '@ember/utils';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import { type FeedbackMessage } from '@upfluence/oss-components/components/o-s-s/input-container';

type SkinType = 'success' | 'error' | 'warning';

export type Alert = {
Expand All @@ -27,11 +29,21 @@ interface UtilsAccountBannerArgs {

canSelectItem?: boolean;
selectableItems?: any[];
feedbackMessage?: FeedbackMessage;
required?: boolean;
}

export default class extends Component<UtilsAccountBannerArgs> {
@tracked displaySelectableItems: boolean = false;

get feedbackMessage(): FeedbackMessage | undefined {
return this.args.feedbackMessage;
}

get isErrored(): boolean {
return this.feedbackMessage?.type === 'error';
}

get disabledClass(): string {
return this.args.disabled ? 'account-banner--disabled' : '';
}
Expand All @@ -41,6 +53,7 @@ export default class extends Component<UtilsAccountBannerArgs> {
}

get borderColorClass(): string {
if (this.isErrored) return 'account-banner--error';
if (this.args.skin) return `account-banner--${this.args.skin}`;
return '';
}
Expand Down
126 changes: 100 additions & 26 deletions tests/integration/components/utils/account-banner-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ module('Integration | Component | utils/account-banner', function (hooks) {
setupRenderingTest(hooks);
setupIntl(hooks);

module('rendering', function () {
test('it renders', async function (assert) {
await render(hbs`<Utils::AccountBanner />`);

assert.dom('.account-banner').exists();
});

test('it forwards HTML attributes', async function (assert) {
await render(hbs`<Utils::AccountBanner data-test="banner" />`);

assert.dom('.account-banner[data-test="banner"]').exists();
});
});

module('modifier classes', function () {
test('it renders with no modifier classes by default', async function (assert) {
await render(hbs`<Utils::AccountBanner />`);
Expand Down Expand Up @@ -57,6 +71,27 @@ module('Integration | Component | utils/account-banner', function (hooks) {
});
});

module('icon / image', function () {
test('it renders @icon', async function (assert) {
await render(hbs`<Utils::AccountBanner @icon="fa-circle" />`);

assert.dom('.upf-badge').exists();
});

test('it renders @image as a badge', async function (assert) {
await render(hbs`<Utils::AccountBanner @image="/some/image.svg" />`);

assert.dom('.icon-in-badge').exists();
});

test('@icon takes precedence over @image', async function (assert) {
await render(hbs`<Utils::AccountBanner @icon="fa-circle" @image="/some/image.svg" />`);

assert.dom('.upf-badge').exists();
assert.dom('.icon-in-badge').doesNotExist();
});
});

module('title', function () {
test('it renders @title', async function (assert) {
await render(hbs`<Utils::AccountBanner @title="title" />`);
Expand Down Expand Up @@ -89,7 +124,7 @@ module('Integration | Component | utils/account-banner', function (hooks) {
test('it renders @subtitle', async function (assert) {
await render(hbs`<Utils::AccountBanner @subtitle="subtitle" />`);

assert.dom('[data-control-name="account-banner-selected-item-label"]').hasText('subtitle');
assert.dom('[data-control-name="account-banner-selected-item-label"]').containsText('subtitle');
});

test('it renders custom-subtitle block', async function (assert) {
Expand All @@ -113,30 +148,36 @@ module('Integration | Component | utils/account-banner', function (hooks) {
assert.dom('.custom-sub').doesNotExist();
});

test('it marks subtitle as required when @required is true', async function (assert) {
await render(hbs`<Utils::AccountBanner @subtitle="subtitle" @required={{true}} />`);

assert.dom('[data-control-name="account-banner-selected-item-label"]').hasText('subtitle *');
});

test('it does not mark subtitle as required when @required is not set', async function (assert) {
await render(hbs`<Utils::AccountBanner @subtitle="subtitle" />`);

assert.dom('[data-control-name="account-banner-selected-item-label"]').hasText('subtitle');
});

test('account-banner__selection is not rendered without subtitle or custom-subtitle', async function (assert) {
await render(hbs`<Utils::AccountBanner />`);

assert.dom('.account-banner__selection').doesNotExist();
});
});

module('actions block', function () {
test('it renders the actions block', async function (assert) {
await render(hbs`
<Utils::AccountBanner>
<:actions><span class="action">action</span></:actions>
</Utils::AccountBanner>
`);
test('unique-account class is applied when canSelectItem is false', async function (assert) {
await render(hbs`<Utils::AccountBanner @subtitle="label" />`);

assert.dom('.action').exists();
assert.dom('.account-banner__selection--unique-account').exists();
});
});

module('dropdown', function () {
module('selection dropdown', function () {
test('chevron is not rendered without canSelectItem', async function (assert) {
await render(hbs`<Utils::AccountBanner @subtitle="label" @selected={{true}} />`);

assert.dom('.fa-chevron-down').doesNotExist();
assert.dom('.fa-chevron-down, [data-icon="chevron-down"]').doesNotExist();
});

test('chevron is not rendered with only 1 item', async function (assert) {
Expand All @@ -149,10 +190,12 @@ module('Integration | Component | utils/account-banner', function (hooks) {
/>
`);

assert.dom('[data-icon="chevron-down"]').doesNotExist();
assert.dom('.fa-chevron-down, [data-icon="chevron-down"]').doesNotExist();
});

test('chevron is rendered with canSelectItem and multiple items', async function (assert) {
this.set('items', SELECTABLE_ITEMS);

await render(hbs`
<Utils::AccountBanner
@subtitle="label"
Expand All @@ -162,7 +205,6 @@ module('Integration | Component | utils/account-banner', function (hooks) {
/>
`);

this.set('items', SELECTABLE_ITEMS);
assert.dom('.fa-chevron-down, [data-icon="chevron-down"]').exists();
});

Expand Down Expand Up @@ -286,11 +328,45 @@ module('Integration | Component | utils/account-banner', function (hooks) {

assert.strictEqual(findAll('.item').length, SELECTABLE_ITEMS.length);
});
});

test('unique-account class applied when canSelectItem is false', async function (assert) {
module('feedback message', function () {
test('it does not render any feedback message by default', async function (assert) {
await render(hbs`<Utils::AccountBanner @subtitle="label" />`);

assert.dom('.account-banner__selection--unique-account').exists();
assert.dom('.account-banner__selection + span').doesNotExist();
});

test('it renders an error feedback message', async function (assert) {
this.set('feedback', { type: 'error', value: 'Required field' });
await render(hbs`<Utils::AccountBanner @subtitle="label" @feedbackMessage={{this.feedback}} />`);

assert.dom('.account-banner__selection + span').exists();
assert.dom('.account-banner__selection + span').hasClass('font-color-error-500');
assert.dom('.account-banner__selection + span').hasText('Required field');
});

test('it renders a warning feedback message', async function (assert) {
this.set('feedback', { type: 'warning', value: 'Be careful' });
await render(hbs`<Utils::AccountBanner @subtitle="label" @feedbackMessage={{this.feedback}} />`);

assert.dom('.account-banner__selection + span').exists();
assert.dom('.account-banner__selection + span').hasClass('font-color-warning-500');
assert.dom('.account-banner__selection + span').hasText('Be careful');
});

test('it applies error border class when feedbackMessage type is error', async function (assert) {
this.set('feedback', { type: 'error', value: 'Required field' });
await render(hbs`<Utils::AccountBanner @subtitle="label" @feedbackMessage={{this.feedback}} />`);

assert.dom('.account-banner').hasClass('account-banner--error');
});

test('it does not apply error border class when feedbackMessage type is warning', async function (assert) {
this.set('feedback', { type: 'warning', value: 'Be careful' });
await render(hbs`<Utils::AccountBanner @subtitle="label" @feedbackMessage={{this.feedback}} />`);

assert.dom('.account-banner').hasNoClass('account-banner--error');
});
});

Expand Down Expand Up @@ -328,17 +404,15 @@ module('Integration | Component | utils/account-banner', function (hooks) {
});
});

module('image / icon', function () {
test('it renders @image as a badge', async function (assert) {
await render(hbs`<Utils::AccountBanner @image="/some/image.svg" />`);

assert.dom('.icon-in-badge').exists();
});

test('it renders @icon', async function (assert) {
await render(hbs`<Utils::AccountBanner @icon="fa-circle" />`);
module('actions block', function () {
test('it renders the actions block', async function (assert) {
await render(hbs`
<Utils::AccountBanner>
<:actions><span class="action">action</span></:actions>
</Utils::AccountBanner>
`);

assert.dom('.upf-badge').exists();
assert.dom('.action').exists();
});
});
});
Loading