Skip to content

Commit

Permalink
feat(multiselect): implementa as definições do AnimaliaDS
Browse files Browse the repository at this point in the history
Implementar as definições do handoff do multiselect no PoMultiselect.

Fixes DTHFUI-7684
  • Loading branch information
anliben committed Oct 27, 2023
1 parent 4d7af5f commit b44215f
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 114 deletions.
2 changes: 1 addition & 1 deletion projects/ui/src/lib/components/po-field/po-field.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import { PoUrlComponent } from './po-url/po-url.component';
import { PoCheckboxModule } from './po-checkbox/po-checkbox.module';
import { PoSwitchModule } from './po-switch/po-switch.module';
import { PoLabelModule } from '../po-label';
import { PoTagComponent, PoTagModule } from '../po-tag';
import { PoTagModule } from '../po-tag';

/**
* @description
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { OverlayModule } from '@angular/cdk/overlay';
import { HttpClient, HttpHandler } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing';
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
import { Observable, of, throwError } from 'rxjs';

import * as UtilsFunction from '../../../utils/util';

import { Renderer2 } from '@angular/core';
import { PoTagComponent } from '../../po-tag/po-tag.component';
import { Renderer2 } from '@angular/core';
import { PoKeyCodeEnum } from '../../../enums/po-key-code.enum';
import { PoFieldContainerComponent } from '../po-field-container/po-field-container.component';
import { PoMultiselectBaseComponent } from '../po-multiselect/po-multiselect-base.component';
import { PoDisclaimerComponent } from './../../po-disclaimer/po-disclaimer.component';
import { PoFieldContainerBottomComponent } from './../po-field-container/po-field-container-bottom/po-field-container-bottom.component';
import { PoMultiselectDropdownComponent } from './po-multiselect-dropdown/po-multiselect-dropdown.component';
import { PoMultiselectFilter } from './po-multiselect-filter.interface';
Expand Down Expand Up @@ -107,7 +106,7 @@ describe('PoMultiselectComponent:', () => {
expect(component.getTagsWidth().length).toBeTruthy();
});

it('should calc visible items with a tiny space', () => {
it('should calc visible items with a tiny space', fakeAsync(() => {
const selectedOptions = [
{ label: 'label', value: 1 },
{ label: 'label', value: 2 },
Expand All @@ -123,15 +122,21 @@ describe('PoMultiselectComponent:', () => {
selectedOptions: selectedOptions,
isCalculateVisibleItems: true,
fieldValue: 'value',
fieldLabel: 'label'
fieldLabel: 'label',
initCalculateItems: true,
handleKeyboardNavigationTag: () => {}
};
spyOn(fakeThis, 'handleKeyboardNavigationTag');

component.calculateVisibleItems.call(fakeThis);

tick(300);

expect(fakeThis.visibleTags.length).toBe(1);
expect(fakeThis.visibleTags[1]).toBe(undefined);
expect(fakeThis.isCalculateVisibleItems).toBeFalsy();
});
expect(fakeThis.handleKeyboardNavigationTag).toHaveBeenCalled();
}));

it('should set focus on input', () => {
component.initialized = false;
Expand Down Expand Up @@ -162,55 +167,6 @@ describe('PoMultiselectComponent:', () => {
expect(component.debounceResize).toHaveBeenCalled();
});

it('shouuld call focusPreviousDisclaimer', () => {
component['focusPreviousDisclaimer'](tags.children, index);

const focusedElement = tags.children[index - 1] as HTMLElement;

expect(tags.contains(focusedElement)).toBe(true);
});

it('should call focusNextDisclaimer', () => {
spyOn(component.inputElement.nativeElement, 'focus');

component['focusNextDisclaimer'](tags.children, index);

expect(component.inputElement.nativeElement.focus).toHaveBeenCalled();
});

it('should call focusNextDisclaimer when index equals 1', () => {
const fakeEvent = { focus: jasmine.createSpy('focus') };
spyOn(tags.children[1 + 1], 'querySelector').and.returnValue(fakeEvent);

component['focusNextDisclaimer'](tags.children, 1);

expect((tags.children[1] as HTMLElement).outerHTML).toBe('<div class="po-tag-remove"></div>');
});

it('should call addKeyListener if keyCode entries [37, 39, 40]', () => {
spyOn(component, 'focusPreviousDisclaimer' as any);
spyOn(component, 'focusNextDisclaimer' as any);
spyOn(component, 'controlDropdownVisibility');

const spanElement = document.createElement('span');

const eventLeft = new KeyboardEvent('keydown', { keyCode: PoKeyCodeEnum.left });
component['addKeyListener'](spanElement, tags, index, eventLeft);
spanElement.dispatchEvent(eventLeft);

const eventRight = new KeyboardEvent('keydown', { keyCode: PoKeyCodeEnum.right });
component['addKeyListener'](spanElement, tags, index, eventRight);
spanElement.dispatchEvent(eventRight);

const eventArrowDown = new KeyboardEvent('keydown', { keyCode: PoKeyCodeEnum.arrowDown });
component['addKeyListener'](spanElement, tags, index, eventArrowDown);
spanElement.dispatchEvent(eventArrowDown);

expect(component['focusPreviousDisclaimer']).toHaveBeenCalledWith(tags, index);
expect(component['focusNextDisclaimer']).toHaveBeenCalledWith(tags, index);
expect(component.controlDropdownVisibility).toHaveBeenCalledWith(true);
});

it('should call preventDefault and controlDropdownVisibility(false) when keyCode esc is pressed', () => {
const event = { preventDefault: jasmine.createSpy(), keyCode: 27 };
spyOn(component, 'controlDropdownVisibility');
Expand All @@ -221,17 +177,21 @@ describe('PoMultiselectComponent:', () => {
expect(component.controlDropdownVisibility).toHaveBeenCalledWith(false);
});

it('should call controlDropdownVisibility arrow to down is pressed', () => {
const fakeEvent = {
preventDefault: () => true,
keyCode: 40,
setFocusOnDropdownListbox: () => {}
};
it('onKeyDown: should call controlDropdownVisibility arrow to down is pressed', () => {
const event = new KeyboardEvent('keydown', { keyCode: PoKeyCodeEnum.arrowDown });
const tagRemovable = document.createElement('span');

tagRemovable.setAttribute('class', 'po-tag-remove');
component.visibleTags = [tagRemovable, tagRemovable];

spyOn(component, 'controlDropdownVisibility');
spyOn(fakeEvent, 'preventDefault');
const onKeyDown = component.onKeyDown(fakeEvent);
spyOn(event, 'preventDefault');

component.controlDropdownVisibility(true);
const onKeyDown = component.onKeyDown(event);

expect(onKeyDown).toBeUndefined();
expect(event.preventDefault).toHaveBeenCalled();
expect(component.controlDropdownVisibility).toHaveBeenCalledWith(true);
});

Expand Down Expand Up @@ -281,24 +241,6 @@ describe('PoMultiselectComponent:', () => {
expect(component.controlDropdownVisibility).not.toHaveBeenCalled();
});

it('should call addKeyListener for PoKeyCodeEnum.left or PoKeyCodeEnum.right', () => {
const event = new KeyboardEvent('keydown', { keyCode: PoKeyCodeEnum.left });
const tagRemovable = document.createElement('span');
tagRemovable.setAttribute('class', 'po-tag-remove');

const tag = document.createElement('span');
tag.setAttribute('class', 'po-tag');
tag.appendChild(tagRemovable);

const disclaimersFake = [tag, tag];
spyOn(component, 'addKeyListener' as any);
spyOn(component['el'].nativeElement, 'querySelectorAll').and.returnValue(disclaimersFake);

component.onKeyDown(event);

expect(component['addKeyListener']).toHaveBeenCalled();
});

it('should return when event keyCode is PoKeyCodeEnum.tab and visibleTags.length > 1', () => {
const event = new KeyboardEvent('keydown', { keyCode: PoKeyCodeEnum.tab });
const tagRemovable = document.createElement('span');
Expand Down Expand Up @@ -381,7 +323,7 @@ describe('PoMultiselectComponent:', () => {
expect(component.visibleOptionsDropdown.length).toBe(1);
});

it('should remove item, call updateVisibleItems and callOnChange', () => {
it('closeTag: shouldnt remove item, call updateVisibleItems and callOnChange', () => {
component.selectedOptions = [
{ label: 'label', value: 1 },
{ label: 'label2', value: 2 }
Expand All @@ -395,6 +337,34 @@ describe('PoMultiselectComponent:', () => {
expect(component.selectedOptions[0].value).toBe(2);
});

it('closeTag: should remove item, call updateVisibleItems and callOnChange', () => {
component.visibleTags = [
{ label: 'label10', value: 10 },
{ label: 'label11', value: 11 },
{ label: 'label12', value: 12 },
{ label: 'label13', value: 13 },
{ label: 'label14', value: 14 }
];
component.selectedOptions = [
{ label: 'label', value: 1 },
{ label: 'label2', value: 2 },
{ label: 'label3', value: 3 },
{ label: 'label4', value: 4 },
{ label: 'label5', value: 5 },
{ label: 'label6', value: 6 },
{ label: 'label7', value: 7 },
{ label: 'label8', value: 8 },
{ label: 'label9', value: 9 }
];

spyOn(component, 'updateVisibleItems');
spyOn(component, 'callOnChange');
component['closeTag']('1+', 'click');
expect(component.updateVisibleItems).toHaveBeenCalled();
expect(component.callOnChange).toHaveBeenCalled();
expect(component.selectedOptions[0].value).toBe(1);
});

it('should call controlDropdownVisibility in wasClickedOnToggle', () => {
component.dropdownOpen = true;
fixture.detectChanges();
Expand Down Expand Up @@ -714,21 +684,49 @@ describe('PoMultiselectComponent:', () => {
expect(closeSpy).toHaveBeenCalled();
});

it('onBlur: should be called when blur event', () => {
component['onModelTouched '] = () => {};
it('onKeyDownDropdown: should control dropdown visibility', () => {
const event = new KeyboardEvent('keydown', { key: 'Escape' });
const controlDropdownVisibilitySpy = spyOn(component, 'controlDropdownVisibility');

component.onKeyDownDropdown(event, 0);

expect(controlDropdownVisibilitySpy).toHaveBeenCalledWith(false);
});

it('onKeyDownDropdown: should do nothing for non-Escape key', () => {
const event = new KeyboardEvent('keydown', { key: 'Enter' });
const preventDefaultSpy = spyOn(event, 'preventDefault');
const controlDropdownVisibilitySpy = spyOn(component, 'controlDropdownVisibility');
const focusSpy = spyOn(component.inputElement.nativeElement, 'focus');

component.onKeyDownDropdown(event, 0);

expect(preventDefaultSpy).not.toHaveBeenCalled();
expect(controlDropdownVisibilitySpy).not.toHaveBeenCalled();
expect(focusSpy).not.toHaveBeenCalled();
});

it('onBlur: should update aria-label if it contains "Unselected"', () => {
const inputEl = component.inputElement.nativeElement;
inputEl.setAttribute('aria-label', 'Unselected');
component.label = 'New Label';
spyOn(component, <any>'onModelTouched');

component.onBlur();

expect(inputEl.getAttribute('aria-label')).toBe('New Label');
expect(component['onModelTouched']).toHaveBeenCalled();
});

it('onBlur: shouldn´t throw error if onModelTouched is falsy', () => {
component['onModelTouched'] = null;
it('onBlur: should not update aria-label if it does not contain "Unselected"', () => {
const inputElement = component.inputElement.nativeElement;
inputElement.setAttribute('aria-label', 'Something Selected');
component.label = 'New Label';
spyOn(component, <any>'onModelTouched');

const fnError = () => component.onBlur();
component.onBlur();

expect(fnError).not.toThrow();
expect(component['onModelTouched']).toHaveBeenCalled();
});

it('debounceResize: should call `calculateVisibleItems` after 200 milliseconds', done => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,20 +459,6 @@ export class PoMultiselectComponent
);
}

private focusItem() {
this.dropdown.listbox.listboxItemList.nativeElement.focus();
setTimeout(() => {
let item;
if (this.selectedOptions) {
item = document.querySelector('.po-listbox-item[aria-selected="true"]');
} else {
item = document.querySelectorAll('.po-listbox-item')[0] as HTMLElement;
}
this.dropdown.listbox.listboxItemList.nativeElement.focus();
item?.focus();
});
}

private applyFilterInFirstClick() {
if (this.isFirstFilter) {
this.isServerSearching = true;
Expand Down Expand Up @@ -533,28 +519,29 @@ export class PoMultiselectComponent
this.subscription.unsubscribe();
this.subscription = new Subscription();
const tagRemoveElements = this.el.nativeElement.querySelectorAll('.po-tag-remove');

const setTabIndex = (index, tabIndex) => {
tagRemoveElements[index].setAttribute('tabindex', tabIndex);
};

tagRemoveElements.forEach((tagRemoveElement, index) => {
if (index === initialIndex) {
tagRemoveElements[initialIndex].setAttribute('tabindex', 0);
} else if (tagRemoveElements.length === initialIndex) {
tagRemoveElements[initialIndex - 1].setAttribute('tabindex', 0);
} else {
tagRemoveElements[index].setAttribute('tabindex', -1);
}
setTabIndex(index, index === initialIndex ? 0 : -1);

this.subscription.add(
fromEvent(tagRemoveElement, 'keydown').subscribe((event: KeyboardEvent) => {
if (event.code === 'Space') {
event.preventDefault();
event.stopPropagation();
}

if (event.key === 'ArrowLeft' && index > 0) {
tagRemoveElements[index].setAttribute('tabindex', -1);
setTabIndex(index, -1);
setTabIndex(index - 1, 0);
tagRemoveElements[index - 1].focus();
tagRemoveElements[index - 1].setAttribute('tabindex', 0);
} else if (event.key === 'ArrowRight' && index < tagRemoveElements.length - 1) {
tagRemoveElements[index].setAttribute('tabindex', -1);
setTabIndex(index, -1);
setTabIndex(index + 1, 0);
tagRemoveElements[index + 1].focus();
tagRemoveElements[index + 1].setAttribute('tabindex', 0);
}
})
);
Expand Down

0 comments on commit b44215f

Please sign in to comment.