diff --git a/tests/dummy/app/templates/visual.hbs b/tests/dummy/app/templates/visual.hbs
index 49f5a677a..0d9f5a749 100644
--- a/tests/dummy/app/templates/visual.hbs
+++ b/tests/dummy/app/templates/visual.hbs
@@ -37,6 +37,16 @@
Multi-skin Progress bars
+
+
+
`);
+
assert.dom('.upf-banner.upf-banner--size-sm').doesNotExist();
assert.dom('.upf-banner.upf-banner--size-lg').doesNotExist();
});
});
+
+ module('@feedbackMessage parameter', function () {
+ module('feedback message margin class', function () {
+ test('When feedback message is passed, it adds a margin-bottom class to accomodate the message', async function (assert) {
+ this.feedbackMessage = {
+ type: 'error',
+ value: 'This is a feedback message'
+ };
+ await render(hbs`
`);
+
+ assert.dom('.upf-banner.margin-bottom-px-24').exists();
+ });
+
+ test('When no feedback message is passed, it does not add a margin-bottom class', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('.upf-banner.margin-bottom-px-24').doesNotExist();
+ });
+ });
+
+ test('the feedback message is rendered below the banner', async function (assert) {
+ this.feedbackMessage = {
+ type: 'error',
+ value: 'This is a feedback message'
+ };
+ await render(hbs`
`);
+
+ assert.dom('.upf-banner--feedback').hasText('This is a feedback message');
+ });
+
+ FEEDBACK_TYPES.forEach((type: FeedbackType) => {
+ test(`When feedback type is ${type}, the border has the corresponding class`, async function (assert) {
+ this.feedbackMessage = {
+ type: type,
+ value: 'This is a feedback message'
+ };
+ await render(hbs`
`);
+
+ assert.dom(`.upf-banner.upf-banner--${type}`).exists();
+ });
+
+ test(`when feedback message is ${type}, the feedback text has the corresponding class`, async function (assert) {
+ this.feedbackMessage = {
+ type: type,
+ value: 'This is a feedback message'
+ };
+ await render(hbs`
`);
+
+ assert.dom('.upf-banner--feedback').hasClass(`font-color-${type}-500`);
+ });
+ });
+
+ module('invalid feedback type', function (hooks) {
+ hooks.beforeEach(function () {
+ this.feedbackMessage = {
+ type: 'invalid-type',
+ value: 'This is a feedback message'
+ };
+ });
+
+ test('when feedback type is invalid, the border does not have any feedback class', async function (assert) {
+ await render(hbs`
`);
+
+ assert
+ .dom('.upf-banner')
+ .doesNotHaveClass('upf-banner--error')
+ .doesNotHaveClass('upf-banner--warning')
+ .doesNotHaveClass('upf-banner--success');
+ });
+
+ test('when feedback type is invalid, the feedback text is not displayed', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('.upf-banner--feedback').doesNotExist();
+ });
+ });
+
+ test('when feedback value is undefined, the feedback text is not displayed but the border color changes accordingly', async function (assert) {
+ this.feedbackMessage = {
+ type: 'success',
+ value: undefined
+ };
+ await render(hbs`
`);
+
+ assert.dom('.upf-banner--feedback').doesNotExist();
+ assert.dom('.upf-banner').hasClass('upf-banner--success');
+ });
+ });
});
diff --git a/tests/integration/components/o-s-s/button-test.ts b/tests/integration/components/o-s-s/button-test.ts
index 30189c303..025038bd8 100644
--- a/tests/integration/components/o-s-s/button-test.ts
+++ b/tests/integration/components/o-s-s/button-test.ts
@@ -50,6 +50,18 @@ module('Integration | Component | o-s-s/button', function (hooks) {
assert.dom('.upf-btn').hasText('Test');
});
+ test('it applies margin-left-px-18 to suffix icon when label is present', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('.upf-btn i.fa-chevron-down').hasClass('margin-left-px-18');
+ });
+
+ test('it does not apply margin to suffix icon when label is absent', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('.upf-btn i.fa-chevron-down').doesNotHaveClass('margin-left-px-18');
+ });
+
module('it renders with the correct skin', function () {
test('when using an unknown skin, it is set to default', async function (assert) {
await render(hbs`
`);
@@ -138,7 +150,7 @@ module('Integration | Component | o-s-s/button', function (hooks) {
assert.dom('.upf-btn i.fas').exists();
assert.dom('.upf-btn i.fas').hasClass('fa-circle-notch');
assert.dom('.upf-btn i.fas').hasClass('fa-spin');
- assert.dom('.upf-btn span.margin-left-px-6').doesNotExist();
+ assert.dom('.upf-btn span').doesNotExist();
});
test('when loading and the showLabel loading option is truthy, the label is displayed', async function (assert) {
@@ -148,8 +160,8 @@ module('Integration | Component | o-s-s/button', function (hooks) {
assert.dom('.upf-btn i.fas').exists();
assert.dom('.upf-btn i.fas').hasClass('fa-circle-notch');
assert.dom('.upf-btn i.fas').hasClass('fa-spin');
- assert.dom('.upf-btn span.margin-left-px-6').exists();
- assert.dom('.upf-btn span.margin-left-px-6').hasText('Test');
+ assert.dom('.upf-btn span').exists();
+ assert.dom('.upf-btn span').hasText('Test');
});
});
diff --git a/tests/integration/components/o-s-s/context-menu-test.ts b/tests/integration/components/o-s-s/context-menu-test.ts
new file mode 100644
index 000000000..c8641f981
--- /dev/null
+++ b/tests/integration/components/o-s-s/context-menu-test.ts
@@ -0,0 +1,242 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { click, render, triggerEvent } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import sinon from 'sinon';
+import { setupIntl } from 'ember-intl/test-support';
+
+module('Integration | Component | o-s-s/context-menu', function (hooks) {
+ setupRenderingTest(hooks);
+ setupIntl(hooks);
+
+ hooks.beforeEach(function () {
+ this.label = 'Open menu';
+ this.onOpenStub = sinon.stub();
+ this.onCloseStub = sinon.stub();
+ });
+
+ test('it renders', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('[data-control-name="context-menu"]').exists();
+ assert.dom('[data-control-name="context-menu"]').hasText(this.label);
+ });
+
+ test('it shows chevron-down when closed and chevron-up when open', async function (assert) {
+ this.items = [{ title: 'Item', action: () => {} }];
+ await render(
+ hbs`
`
+ );
+ assert.dom('button i.fa-chevron-down').exists();
+ assert.dom('button i.fa-chevron-up').doesNotExist();
+ await click('button');
+ assert.dom('button i.fa-chevron-up').exists();
+ assert.dom('button i.fa-chevron-down').doesNotExist();
+ });
+
+ test('it accepts chevronGap argument', async function (assert) {
+ this.items = [{ title: 'Item', action: () => {} }];
+ await render(
+ hbs`
`
+ );
+ assert.dom('button .fx-gap-px-12').exists();
+ });
+
+ module('It accepts same arguments as OSSButton component', function () {
+ test('it accepts label argument', async function (assert) {
+ this.label = 'first';
+ await render(hbs`
`);
+ assert.dom('button').hasText('first');
+ this.set('label', 'secondary');
+ assert.dom('button').hasText('secondary');
+ });
+
+ test('it accepts skin argument', async function (assert) {
+ this.skin = 'primary';
+ await render(hbs`
`);
+ assert.dom('button').hasClass('upf-btn--primary');
+ this.set('skin', 'secondary');
+ assert.dom('button').hasClass('upf-btn--secondary');
+ });
+
+ test('it accepts size argument', async function (assert) {
+ this.size = 'sm';
+ await render(hbs`
`);
+ assert.dom('button').hasClass('upf-btn--sm');
+ this.set('size', 'md');
+ assert.dom('button').hasClass('upf-btn--md');
+ });
+
+ test('it accepts loading argument', async function (assert) {
+ this.loading = true;
+ await render(
+ hbs`
`
+ );
+ assert.dom('button i').hasClass('fa-circle-notch').hasClass('fa-spin');
+ this.set('loading', false);
+ assert.dom('button i.fa-circle-notch').doesNotExist();
+ assert.dom('button i.fa-chevron-down').exists();
+ });
+
+ test('it accepts loadingOptions argument', async function (assert) {
+ this.loadingOptions = { showLabel: true };
+ await render(
+ hbs`
`
+ );
+ assert.dom('button i').hasClass('fa-circle-notch').hasClass('fa-spin');
+ assert.dom('button').hasText(this.label);
+ this.set('loadingOptions', { showLabel: false });
+ assert.dom('button').hasNoText();
+ });
+
+ test('it accepts icon argument', async function (assert) {
+ this.icon = 'fa-plane';
+ await render(hbs`
`);
+ assert.dom('button i').hasClass('fa-plane');
+ this.set('icon', 'fa-plus');
+ assert.dom('button i').hasClass('fa-plus');
+ });
+
+ test('it accepts iconUrl argument', async function (assert) {
+ this.iconUrl = '/@upfluence/oss-components/assets/heart.svg';
+ await render(
+ hbs`
`
+ );
+ assert.dom('button img').hasAttribute('src', this.iconUrl);
+ });
+
+ test('it accepts theme argument', async function (assert) {
+ this.theme = 'light';
+ await render(
+ hbs`
`
+ );
+ assert.dom('button').hasNoClass('upf-btn--dark-bg');
+ this.set('theme', 'dark');
+ assert.dom('button').hasClass('upf-btn--dark-bg');
+ });
+
+ test('it accepts square argument', async function (assert) {
+ this.square = true;
+ await render(
+ hbs`
`
+ );
+ assert.dom('button').hasClass('upf-square-btn');
+ this.set('square', false);
+ assert.dom('button').doesNotHaveClass('upf-square-btn');
+ });
+
+ test('it accepts countDown argument', async function (assert) {
+ const clock = sinon.useFakeTimers({
+ shouldAdvanceTime: true
+ });
+
+ this.countDown = { callback: sinon.stub(), time: 50, step: 10 };
+ await render(
+ hbs`
`
+ );
+ await click('button');
+ assert.true(this.countDown.callback.notCalled);
+ clock.tick(100);
+ assert.true(this.countDown.callback.calledOnce);
+ clock.restore();
+ });
+ });
+
+ module('When clicking on the button', function () {
+ test('it opens the panel', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('.context-menu-panel__scrollable-container').doesNotExist();
+ await click('button');
+ assert.dom('.context-menu-panel__scrollable-container').exists();
+ });
+
+ test('it trigger the onMenuOpened callback', async function (assert) {
+ await render(hbs`
`);
+
+ assert.ok(this.onOpenStub.notCalled);
+ await click('button');
+ assert.ok(this.onOpenStub.calledOnce);
+ });
+
+ module('When clicking a second time on the button', function () {
+ test('it closes the panel', async function (assert) {
+ await render(hbs`
`);
+ await click('button');
+ assert.dom('.context-menu-panel__scrollable-container').exists();
+ await click('button');
+ assert.dom('.context-menu-panel__scrollable-container').doesNotExist();
+ });
+
+ test('it trigger the onMenuOpened callback', async function (assert) {
+ await render(hbs`
`);
+ await click('button');
+ assert.ok(this.onCloseStub.notCalled);
+ await click('button');
+ assert.ok(this.onCloseStub.calledOnce);
+ });
+ });
+ });
+
+ module('When the panel is opened', function () {
+ test('On click outside of the panel, it closes', async function (assert) {
+ await render(hbs`
+
+
`);
+ await click('button');
+ assert.ok(this.onCloseStub.notCalled);
+ await click('[data-control-name="outside-container"]');
+ assert.ok(this.onCloseStub.calledOnce);
+ });
+
+ module('CloseOnMouseLeave', () => {
+ test('When closeOnMouseLeave is true, on mouse leave it closes the panel', async function (assert) {
+ await render(hbs`
`);
+ await click('button');
+ assert.dom('.context-menu-panel__scrollable-container').exists();
+ await triggerEvent('.context-menu-panel__scrollable-container', 'mouseleave');
+ assert.dom('.context-menu-panel__scrollable-container').doesNotExist();
+ assert.ok(this.onCloseStub.calledOnce);
+ });
+
+ test('When closeOnMouseLeave is false, on mouse leave it does not close the panel', async function (assert) {
+ await render(hbs`
`);
+ await click('button');
+ assert.dom('.context-menu-panel__scrollable-container').exists();
+ await triggerEvent('.context-menu-panel__scrollable-container', 'mouseleave');
+ assert.dom('.context-menu-panel__scrollable-container').exists();
+ assert.ok(this.onCloseStub.notCalled);
+ });
+ });
+ });
+
+ test('When button is loading, clicking does nothing', async function (assert) {
+ await render(hbs`
`);
+
+ assert.ok(this.onOpenStub.notCalled);
+ await click('button');
+ assert.ok(this.onOpenStub.notCalled);
+ });
+});
diff --git a/tests/integration/components/o-s-s/context-menu/panel-test.ts b/tests/integration/components/o-s-s/context-menu/panel-test.ts
new file mode 100644
index 000000000..d470600c2
--- /dev/null
+++ b/tests/integration/components/o-s-s/context-menu/panel-test.ts
@@ -0,0 +1,266 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { click, render, settled, triggerEvent, waitUntil } from '@ember/test-helpers';
+import { hbs, type TemplateFactory } from 'ember-cli-htmlbars';
+import { setupIntl } from 'ember-intl/test-support';
+import sinon from 'sinon';
+import { setComponentTemplate } from '@ember/component';
+import Component from '@glimmer/component';
+import { SUBPANEL_OFFSET } from '@upfluence/oss-components/components/o-s-s/context-menu/panel';
+
+module('Integration | Component | o-s-s/context-menu/panel', function (hooks) {
+ setupRenderingTest(hooks);
+ setupIntl(hooks);
+
+ hooks.beforeEach(function () {
+ this.subItems = [
+ { title: 'Sub Item 1.1', action: sinon.stub() },
+ { title: 'Sub Item 1.2', action: sinon.stub() },
+ { title: 'Sub Item 1.3', action: sinon.stub() }
+ ];
+ this.items = [
+ { title: 'Item 1', action: sinon.stub(), items: this.subItems },
+ {
+ title: 'Item 2',
+ action: sinon.stub()
+ }
+ ];
+
+ this.onCloseStub = sinon.stub();
+ });
+
+ test('it renders properly', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('.context-menu-panel__dropdown').exists();
+ assert.dom('.context-menu-panel__dropdown li .oss-infinite-select-option').exists({ count: 2 });
+ });
+
+ test('When referenceTarget is passed, it attaches and moves with the target', async function (assert) {
+ await render(hbs`
+ {{#if this.isInitialized}}
+
+ {{/if}}
+
+
+ `);
+
+ const buttonRef = document.querySelector('#second_button') as HTMLElement;
+
+ this.set('referenceTarget', buttonRef);
+ this.set('isInitialized', true);
+ await settled();
+ const panelRef = document.querySelector('.context-menu-panel__scrollable-container') as HTMLElement;
+ assert.equal(panelRef.style.top, '36px');
+ await waitUntil(() => panelRef.style.left === '63.5px', { timeout: 300 });
+ assert.equal(panelRef.style.left, '63.5px');
+ buttonRef.style.marginLeft = '50px';
+ await settled();
+ assert.equal(panelRef.style.top, '36px');
+ await waitUntil(() => panelRef.style.left === '113.5px', { timeout: 300 });
+ assert.equal(panelRef.style.left, '113.5px');
+ });
+
+ module('placement', function (hooks) {
+ test('When placement is set to right-start, it positions the panel accordingly', async function (assert) {
+ await render(hbs`
+ {{#if this.isInitialized}}
+
+ {{/if}}
+
+ `);
+
+ const buttonRef = document.querySelector('#trigger') as HTMLElement;
+
+ this.set('referenceTarget', buttonRef);
+ this.set('isInitialized', true);
+ const panelRef = document.querySelector('.context-menu-panel__scrollable-container') as HTMLElement;
+ const qunitTestContainer = buttonRef.offsetParent as HTMLElement;
+ const leftPosition = buttonRef.getBoundingClientRect().left - qunitTestContainer.getBoundingClientRect().left;
+ await settled();
+ const expectedLeftPosition =
+ Number(((leftPosition + buttonRef.getBoundingClientRect().width) * 2).toFixed(4)) + 'px';
+ const expectedTopPosition = 0 + 'px';
+ assert.equal(expectedLeftPosition, panelRef.style.left);
+ assert.equal(expectedTopPosition, panelRef.style.top);
+ });
+
+ test('When placement is set to bottom-start, it positions the panel accordingly', async function (assert) {
+ await render(hbs`
+ {{#if this.isInitialized}}
+
+ {{/if}}
+
+ `);
+
+ const buttonRef = document.querySelector('#trigger') as HTMLElement;
+
+ this.set('referenceTarget', buttonRef);
+ this.set('isInitialized', true);
+ const panelRef = document.querySelector('.context-menu-panel__scrollable-container') as HTMLElement;
+ const qunitTestContainer = buttonRef.offsetParent as HTMLElement;
+ const topPosition = buttonRef.getBoundingClientRect().top - qunitTestContainer.getBoundingClientRect().top;
+ await settled();
+ const expectedLeftPosition = 0 + 'px';
+ const expectedTopPosition =
+ Number(((topPosition + buttonRef.getBoundingClientRect().height) * 2).toFixed(4)) + 'px';
+ assert.equal(expectedLeftPosition, panelRef.style.left);
+ assert.equal(expectedTopPosition, panelRef.style.top);
+ });
+ });
+
+ module('Offset', function () {
+ test('When offset is 0, panel is stuck to his reference target', async function (assert) {
+ this.offset = 0;
+ await render(hbs`
+ {{#if this.isInitialized}}
+
+ {{/if}}
+
+ `);
+
+ const buttonRef = document.querySelector('#trigger') as HTMLElement;
+
+ this.set('referenceTarget', buttonRef);
+ this.set('isInitialized', true);
+ const panelRef = document.querySelector('.context-menu-panel__scrollable-container') as HTMLElement;
+ await settled();
+ assert.equal(panelRef.style.top, '36px');
+ });
+
+ test('When offset is defined, panel is is moved by that many pixels than defined to his reference target', async function (assert) {
+ this.offset = 20;
+ await render(hbs`
+ {{#if this.isInitialized}}
+
+ {{/if}}
+
+ `);
+
+ const buttonRef = document.querySelector('#trigger') as HTMLElement;
+
+ this.set('referenceTarget', buttonRef);
+ this.set('isInitialized', true);
+ const panelRef = document.querySelector('.context-menu-panel__scrollable-container') as HTMLElement;
+ await settled();
+ assert.equal(panelRef.style.top, '56px');
+ });
+ });
+
+ module('When items are passed', function () {
+ module('if items has subitems', function () {
+ test('Clicking on item opens a submenu on the right of the trigger element', async function (assert) {
+ await render(hbs`
+ {{#if this.isInitialized}}
+
+ {{/if}}
+
+ `);
+
+ const buttonRef = document.querySelector('#trigger') as HTMLElement;
+
+ this.set('referenceTarget', buttonRef);
+ this.set('isInitialized', true);
+
+ assert.dom('div.context-menu-panel__scrollable-container').exists({ count: 1 });
+ await click('div.context-menu-panel__scrollable-container li:nth-of-type(1) .oss-infinite-select-option');
+ assert.dom('div.context-menu-panel__scrollable-container').exists({ count: 2 });
+ const panels = document.querySelectorAll('div.context-menu-panel__scrollable-container');
+ const triggerPosition = (
+ panels[0]?.querySelector('div.context-menu-panel__dropdown li:nth-of-type(1)') as HTMLElement
+ ).getBoundingClientRect();
+ assert.equal(triggerPosition.x + triggerPosition.width, panels[1]?.getBoundingClientRect().x);
+ assert.equal(triggerPosition.y + SUBPANEL_OFFSET / 2, panels[1]?.getBoundingClientRect().y);
+ });
+
+ test('Submenu items are properly displayed', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('div.context-menu-panel__dropdown').exists({ count: 1 });
+ await click('div.context-menu-panel__dropdown li:nth-of-type(1) .oss-infinite-select-option');
+ assert.dom('div.context-menu-panel__dropdown').exists({ count: 2 });
+ const panels = document.querySelectorAll('div.context-menu-panel__dropdown');
+ assert.equal(panels[1]?.querySelectorAll('li').length, 3);
+ assert.equal(
+ (panels[1]?.querySelector('li:nth-of-type(1)') as HTMLElement).textContent?.trim(),
+ 'Sub Item 1.1'
+ );
+ assert.equal(
+ (panels[1]?.querySelector('li:nth-of-type(2)') as HTMLElement).textContent?.trim(),
+ 'Sub Item 1.2'
+ );
+ assert.equal(
+ (panels[1]?.querySelector('li:nth-of-type(3)') as HTMLElement).textContent?.trim(),
+ 'Sub Item 1.3'
+ );
+ });
+ });
+
+ test('If item has custom component it render the component instead of default one', async function (assert) {
+ class TestComponent extends Component {}
+ setComponentTemplate(hbs`
{{@item.title}}
` as any, TestComponent);
+ const component = this.owner.register('component:test-component', TestComponent);
+
+ this.items = [{ title: 'custom', action: () => console.log('Item 1 clicked'), rowRenderer: TestComponent }];
+ await render(hbs`
`);
+
+ assert.dom('.context-menu-panel__dropdown li [data-control-name="custom-row"]').exists();
+ });
+ });
+
+ test('When no items are passed, it displays the empty state', async function (assert) {
+ await render(hbs`
`);
+
+ assert.dom('.upf-infinite-select__items-container--empty').exists();
+ });
+
+ test('When mouse leave the panel, it triggers onMouseLeave action', async function (assert) {
+ this.onMouseLeaveStub = sinon.stub();
+ await render(hbs`
`);
+ assert.ok(this.onMouseLeaveStub.notCalled);
+ await triggerEvent('.context-menu-panel__dropdown', 'mouseleave');
+ assert.ok(this.onMouseLeaveStub.calledOnce);
+ });
+
+ module('When clicking on an item', function () {
+ test('It triggers the item action', async function (assert) {
+ await render(hbs`
`);
+
+ assert.ok(this.items[1].action.notCalled);
+ await click('div.context-menu-panel__dropdown li:nth-of-type(2) .oss-infinite-select-option');
+ assert.ok(this.items[1].action.calledOnce);
+ });
+
+ test('When action returns false, the menu should stay open', async function (assert) {
+ this.items[1].action.returns(false);
+ await render(hbs`
`);
+
+ assert.ok(this.onCloseStub.notCalled);
+ await click('div.context-menu-panel__dropdown li:nth-of-type(2) .oss-infinite-select-option');
+ assert.ok(this.onCloseStub.notCalled);
+ });
+
+ test('When action returns true or undefined, the menu should close', async function (assert) {
+ await render(hbs`
`);
+
+ assert.ok(this.onCloseStub.notCalled);
+ await click('div.context-menu-panel__dropdown li:nth-of-type(2) .oss-infinite-select-option');
+ assert.ok(this.onCloseStub.calledOnce);
+ });
+ });
+});
diff --git a/tests/integration/components/o-s-s/infinite-select-test.js b/tests/integration/components/o-s-s/infinite-select-test.js
index 30edb3b82..1dd910487 100644
--- a/tests/integration/components/o-s-s/infinite-select-test.js
+++ b/tests/integration/components/o-s-s/infinite-select-test.js
@@ -23,6 +23,16 @@ const FAKE_DATA = [
{ name: 'Wolverine', characters: 'James Howlett' }
];
+const FAKE_DATA_GROUPED = [
+ { name: 'banana', label: 'banana', groupKey: 'fruit' },
+ { name: 'lettuce', label: 'lettuce', groupKey: 'vegetable' },
+ { name: 'orange', label: 'orange', groupKey: 'fruit' },
+ { name: 'carrot', label: 'carrot', groupKey: 'vegetable' },
+ { name: 'apple', label: 'apple', groupKey: 'fruit' },
+ { name: 'spinach', label: 'spinach', groupKey: 'vegetable' },
+ { name: 'other', label: 'other' }
+];
+
module('Integration | Component | o-s-s/infinite-select', function (hooks) {
setupRenderingTest(hooks);
setupIntl(hooks);
@@ -508,4 +518,72 @@ module('Integration | Component | o-s-s/infinite-select', function (hooks) {
});
});
});
+
+ module('When data has groupKey', function (hooks) {
+ hooks.beforeEach(function () {
+ this.items = FAKE_DATA_GROUPED;
+ this.onSelect = () => {};
+ });
+
+ test('For each different groupKey, a group is created', async function (assert) {
+ await renderGrouped();
+
+ assert.dom('.upf-infinite-select__items-container ul').exists({ count: 3 });
+ assert
+ .dom('.upf-infinite-select__items-container ul:nth-of-type(1)')
+ .hasAttribute('data-control-name', 'infinite-select-group-fruit');
+ assert
+ .dom('.upf-infinite-select__items-container ul:nth-of-type(2)')
+ .hasAttribute('data-control-name', 'infinite-select-group-vegetable');
+ assert
+ .dom('.upf-infinite-select__items-container ul:nth-of-type(3)')
+ .hasAttribute('data-control-name', 'infinite-select-group-_ungrouped_');
+ });
+
+ test('Items are placed in their respective group', async function (assert) {
+ await renderGrouped();
+
+ const groupFruitItems = Array.from(
+ document.querySelectorAll('.upf-infinite-select__items-container ul:nth-of-type(1) .upf-infinite-select__item')
+ ).map((el) => el.textContent.trim());
+ const groupVegetableItems = Array.from(
+ document.querySelectorAll('.upf-infinite-select__items-container ul:nth-of-type(2) .upf-infinite-select__item')
+ ).map((el) => el.textContent.trim());
+ const groupUngroupedItems = Array.from(
+ document.querySelectorAll('.upf-infinite-select__items-container ul:nth-of-type(3) .upf-infinite-select__item')
+ ).map((el) => el.textContent.trim());
+
+ assert.deepEqual(groupFruitItems, ['banana', 'orange', 'apple']);
+ assert.deepEqual(groupVegetableItems, ['lettuce', 'carrot', 'spinach']);
+ assert.deepEqual(groupUngroupedItems, ['other']);
+ });
+
+ module('Separators', function () {
+ test('A separator is rendered between each group', async function (assert) {
+ await renderGrouped();
+
+ assert.dom('.upf-infinite-select__items-container hr.group-separator').exists({ count: 2 });
+ });
+
+ test('There is no separator after the last group', async function (assert) {
+ await renderGrouped();
+
+ assert.dom('.upf-infinite-select__items-container ul:last-of-type + hr.group-separator').doesNotExist();
+ });
+ });
+ });
+
+ async function renderGrouped(): Promise
{
+ await render(
+ hbs`
+ <:option as |item index|>
+ {{item.label}}
+
+ `
+ );
+ }
});
diff --git a/tests/integration/components/o-s-s/nav-tab-test.ts b/tests/integration/components/o-s-s/nav-tab-test.ts
index 8a6cfb1ef..1944589db 100644
--- a/tests/integration/components/o-s-s/nav-tab-test.ts
+++ b/tests/integration/components/o-s-s/nav-tab-test.ts
@@ -58,6 +58,18 @@ module('Integration | Component | o-s-s/nav-tab', function (hooks) {
assert.dom('.tab-content .fas.fa-circle').exists();
});
+ test('Tab tag displays properly', async function (assert) {
+ this.tabArray.push({ label: 'Tab', tag: { label: '1', skin: 'danger' } });
+
+ await render(hbs``);
+
+ assert.dom('.tab-container').exists();
+ assert.dom('.tab-container .tab').exists();
+ assert.dom('.tab-container .upf-tag').exists();
+ assert.dom('.tab-container .upf-tag').hasText('1');
+ assert.dom('.tab-container .upf-tag').hasClass('upf-tag--critical');
+ });
+
test('Tab displays selected state properly', async function (assert) {
this.tabArray.push({ label: 'Tab', selected: true });
await render(hbs``);
diff --git a/tests/integration/components/o-s-s/progress-bar-test.ts b/tests/integration/components/o-s-s/progress-bar-test.ts
index 3a84c2f53..85d6371f6 100644
--- a/tests/integration/components/o-s-s/progress-bar-test.ts
+++ b/tests/integration/components/o-s-s/progress-bar-test.ts
@@ -186,13 +186,13 @@ module('Integration | Component | o-s-s/progress-bar', function (hooks) {
module('@skins arg behaviour', function () {
test('if the value is defined, the progress bar has the correct multi skin class', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar').hasClass('oss-progress-bar--multi-skin');
});
test('if the value is defined, the progress bar has the correct success, warning, and danger classes', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner').hasClass('oss-progress-bar__inner--pending');
assert.dom('.oss-progress-bar__inner:nth-of-type(2)').hasClass('oss-progress-bar__inner--success');
@@ -200,26 +200,35 @@ module('Integration | Component | o-s-s/progress-bar', function (hooks) {
assert.dom('.oss-progress-bar__inner:nth-of-type(4)').hasClass('oss-progress-bar__inner--danger');
});
+ test('if the values are equals to 0, it renders an empty progress bar', async function (assert) {
+ this.skins = { pending: 0, success: 0, warning: 0, danger: 0 };
+ await render(hbs``);
+
+ assert
+ .dom('.oss-progress-bar .oss-progress-bar__inner')
+ .hasAttribute('style', 'width: 0%;--progress-bar-animation-width: 0%');
+ });
+
test('if the value is defined, the progress bar has the correct pending value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--pending').hasAttribute('aria-valuenow', '10');
});
test('if the value is defined, the progress bar has the correct success value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--success').hasAttribute('aria-valuenow', '30');
});
test('if the value is defined, the progress bar has the correct warning value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--warning').hasAttribute('aria-valuenow', '25');
});
test('if the value is defined, the progress bar has the correct danger value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--danger').hasAttribute('aria-valuenow', '15');
});
@@ -232,13 +241,11 @@ module('Integration | Component | o-s-s/progress-bar', function (hooks) {
);
});
- await render(
- hbs``
- );
+ await render(hbs``);
});
test('if the value is updated, the classes also update', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
this.set('skins', { success: 50, warning: 30, danger: 20 });
@@ -249,28 +256,28 @@ module('Integration | Component | o-s-s/progress-bar', function (hooks) {
});
test('if only the pending value is defined, the progress bar has the correct pending value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--pending').exists();
assert.dom('.oss-progress-bar__inner--pending').hasAttribute('aria-valuenow', '33');
});
test('if only the success value is defined, the progress bar has the correct success value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--success').exists();
assert.dom('.oss-progress-bar__inner--success').hasAttribute('aria-valuenow', '33');
});
test('if only the warning value is defined, the progress bar has the correct warning value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--warning').exists();
assert.dom('.oss-progress-bar__inner--warning').hasAttribute('aria-valuenow', '33');
});
test('if only the danger value is defined, the progress bar has the correct danger value', async function (assert) {
- await render(hbs``);
+ await render(hbs``);
assert.dom('.oss-progress-bar__inner--danger').exists();
assert.dom('.oss-progress-bar__inner--danger').hasAttribute('aria-valuenow', '33');
diff --git a/tests/integration/components/o-s-s/text-area-test.ts b/tests/integration/components/o-s-s/text-area-test.ts
index 03c976a70..6c75bf464 100644
--- a/tests/integration/components/o-s-s/text-area-test.ts
+++ b/tests/integration/components/o-s-s/text-area-test.ts
@@ -107,5 +107,45 @@ module('Integration | Component | o-s-s/text-area', function (hooks) {
this.resize = 'NotACorrectValue';
await render(hbs``);
});
+
+ test('when @hasError is true, textarea has errored class', async function (assert) {
+ await render(hbs``);
+ assert.dom(this.textareaSelector).hasClass('oss-textarea--errored');
+ assert.dom('.oss-textarea-container').hasClass('oss-textarea-container--errored');
+ });
+
+ test('when @hasError is false, textarea does not have errored class', async function (assert) {
+ await render(hbs``);
+ assert.dom(this.textareaSelector).hasNoClass('oss-textarea--errored');
+ assert.dom('.oss-textarea-container').hasNoClass('oss-textarea-container--errored');
+ });
+
+ test('when @errorMessage is provided, textarea has errored class', async function (assert) {
+ await render(hbs``);
+ assert.dom(this.textareaSelector).hasClass('oss-textarea--errored');
+ assert.dom('.oss-textarea-container').hasClass('oss-textarea-container--errored');
+ assert.dom('.font-color-error-500').exists();
+ assert.dom('.font-color-error-500').hasText('This is an error');
+ });
+
+ test('when @errorMessage is empty string, textarea does not have errored class', async function (assert) {
+ await render(hbs``);
+ assert.dom(this.textareaSelector).hasNoClass('oss-textarea--errored');
+ assert.dom('.oss-textarea-container').hasNoClass('oss-textarea-container--errored');
+ });
+
+ test('when both @hasError and @errorMessage are provided, textarea has errored class', async function (assert) {
+ await render(hbs``);
+ assert.dom(this.textareaSelector).hasClass('oss-textarea--errored');
+ assert.dom('.oss-textarea-container').hasClass('oss-textarea-container--errored');
+ assert.dom('.font-color-error-500').exists();
+ assert.dom('.font-color-error-500').hasText('Error message');
+ });
+
+ test('when neither @hasError nor @errorMessage are provided, textarea does not have errored class', async function (assert) {
+ await render(hbs``);
+ assert.dom(this.textareaSelector).hasNoClass('oss-textarea--errored');
+ assert.dom('.oss-textarea-container').hasNoClass('oss-textarea-container--errored');
+ });
});
});