Skip to content

Commit

Permalink
feat(): next (#1609)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidkbh authored Oct 24, 2024
2 parents 8d4b1ba + d0e55bd commit 286c486
Show file tree
Hide file tree
Showing 40 changed files with 76,321 additions and 58,701 deletions.
10,745 changes: 8,046 additions & 2,699 deletions package-lock.json

Large diffs are not rendered by default.

123,652 changes: 67,701 additions & 55,951 deletions projects/demo/assets/documentation.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
NovoOptionSelectionChange,
NovoOverlayTemplateComponent,
NOVO_OPTION_PARENT_COMPONENT,
NOVO_OVERLAY_CONTAINER,
} from 'novo-elements/elements/common';
import { NovoFieldControl, NovoFieldElement, NOVO_FORM_FIELD } from 'novo-elements/elements/field';

Expand Down Expand Up @@ -68,7 +69,10 @@ const NovoAutocompleteMixins: HasOverlayCtor & CanDisableCtor & typeof NovoAutoc
// consumer may have provided, while still being able to receive focus.
'[attr.tabindex]': 'disabled ? null : -1',
},
providers: [{ provide: NOVO_OPTION_PARENT_COMPONENT, useExisting: NovoAutocompleteElement }],
providers: [
{ provide: NOVO_OPTION_PARENT_COMPONENT, useExisting: NovoAutocompleteElement },
{ provide: NOVO_OVERLAY_CONTAINER, useExisting: NovoAutocompleteElement }
],
exportAs: 'novoAutocomplete',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
Expand Down
48 changes: 46 additions & 2 deletions projects/novo-elements/src/elements/button/Button.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// NG2
import { TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
// APP
import { NovoButtonElement } from './Button';
import { Component, Input } from '@angular/core';

describe('Elements: NovoButtonElement', () => {
let fixture;
let fixture: ComponentFixture<NovoButtonElement>;
let component;

beforeAll(() => {
Expand All @@ -21,4 +23,46 @@ describe('Elements: NovoButtonElement', () => {
it('should be compiled', () => {
expect(component).toBeDefined();
});

it('should not assign disabled attribute to self when registered as <novo-button>', () => {
fixture.componentRef.setInput('disabled', true);
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.getAttribute('disabled')).toBe(null);
});
});

@Component({
template: '<button theme="dialogue" [disabled]="disableButton"></button>',
selector: 'test-button-component',
})
class TestButtonContainer {
@Input() disableButton = false;
}

describe('<button> with NovoButtonElement directive', () => {
let fixture: ComponentFixture<TestButtonContainer>;
let component;

beforeAll(() => {
TestBed.configureTestingModule({
declarations: [NovoButtonElement, TestButtonContainer],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(TestButtonContainer);
component = fixture.componentInstance;
});

it('should be compiled', () => {
fixture.detectChanges();
expect(component).toBeDefined();
expect(fixture.debugElement.query(By.directive(NovoButtonElement))).toBeTruthy();
});

it('should disable the element when the disabled property is specified', () => {
fixture.componentRef.setInput('disableButton', true);
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('button')).nativeElement.disabled).toBe(true);
});
});
13 changes: 11 additions & 2 deletions projects/novo-elements/src/elements/button/Button.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// NG2
import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, HostListener, Input } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, HostListener, Input, OnChanges, SimpleChanges } from '@angular/core';
import { BooleanInput, Helpers, Key } from 'novo-elements/utils';

@Component({
Expand Down Expand Up @@ -63,7 +63,7 @@ import { BooleanInput, Helpers, Key } from 'novo-elements/utils';
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NovoButtonElement {
export class NovoButtonElement implements OnChanges {
/**
* The text color of the button. Should be used for Icon buttons. see theme.
*/
Expand Down Expand Up @@ -107,10 +107,19 @@ export class NovoButtonElement {
@HostBinding('class.novo-button-disabled')
disabled: boolean = false;

@HostBinding('attr.disabled')
disabledAttr: undefined | '' = undefined;

private _icon: string;

constructor(public element: ElementRef) {}

ngOnChanges(changes: SimpleChanges): void {
if (changes.disabled && this.element.nativeElement.tagName === 'BUTTON') {
this.disabledAttr = changes.disabled.currentValue ? '' : undefined;
}
}

@HostListener('keydown', ['$event'])
handleKeydown(event: KeyboardEvent) {
if ((Key.Enter === event.key || Key.Space === event.key) && (this.disabled || this.loading)) {
Expand Down
60 changes: 60 additions & 0 deletions projects/novo-elements/src/elements/chips/ChipInput.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Component } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { NovoFieldElement, NovoFieldModule } from '../field';
import { NovoChipInput } from './ChipInput';
import { NovoChipsModule } from './Chips.module';

@Component({
selector: 'test-chip-input',
template: `
<novo-field>
<novo-chip-list [formControl]="chipsCtrl">
<input novoChipInput
[formControl]="textCtrl">
</novo-chip-list>
</novo-field>
<div class="mock-overlay"></div>`
})
class TestChipInputComponent {
textCtrl = new FormControl('');
chipsCtrl = new FormControl([]);
}

describe('Directive: NovoChipInput', () => {
let fixture: ComponentFixture<TestChipInputComponent>;
let testComponent: TestChipInputComponent;
let directive: NovoChipInput;
let mockOverlay: HTMLElement;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [FormsModule, NovoChipsModule, ReactiveFormsModule, NovoFieldModule],
declarations: [TestChipInputComponent]
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(TestChipInputComponent);
testComponent = fixture.debugElement.componentInstance;
directive = fixture.debugElement.query(By.directive(NovoChipInput)).injector.get(NovoChipInput);
mockOverlay = fixture.debugElement.query(By.css('.mock-overlay')).nativeElement;
});

it('should create', () => {
expect(directive).toBeTruthy();
});

it('should clear the control value on blur', () => {
fixture.detectChanges();
const blurEvent = new FocusEvent('blur', {
relatedTarget: mockOverlay
});
testComponent.textCtrl.setValue('value');
spyOn(NovoFieldElement.prototype, 'blurEventIsInField').and.returnValue(false);
fixture.debugElement.query(By.directive(NovoChipInput)).triggerEventHandler('blur', blurEvent);
expect(NovoFieldElement.prototype.blurEventIsInField).toHaveBeenCalledWith(blurEvent);
expect(testComponent.textCtrl.value).toBeFalsy();
});
});
34 changes: 23 additions & 11 deletions projects/novo-elements/src/elements/chips/ChipInput.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { hasModifierKey } from '@angular/cdk/keycodes';
import { Directive, ElementRef, EventEmitter, forwardRef, Inject, Input, OnChanges, Output } from '@angular/core';
import { AfterViewInit, Directive, ElementRef, EventEmitter, forwardRef, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self } from '@angular/core';
import { NgControl } from '@angular/forms';
import { Key, KeyCodes } from 'novo-elements/utils';
import { NovoChipsDefaultOptions, NOVO_CHIPS_DEFAULT_OPTIONS } from './ChipDefaults';
import { NovoChipList } from './ChipList';
import { NovoChipTextControl } from './ChipTextControl';
import { NovoFieldElement } from 'novo-elements/elements/field';
import { expand, first, firstValueFrom, fromEvent, merge, mergeMap, Subject, takeUntil } from 'rxjs';

/** Represents an input event on a `novoChipInput`. */
export interface NovoChipInputEvent {
Expand All @@ -29,7 +31,7 @@ let nextUniqueId = 0;
host: {
class: 'novo-chip-input novo-input-element',
'(keydown)': '_keydown($event)',
'(blur)': '_blur()',
'(blur)': '_blur($event)',
'(focus)': '_focus()',
'(input)': '_onInput()',
'[id]': 'id',
Expand All @@ -39,7 +41,7 @@ let nextUniqueId = 0;
'[attr.aria-required]': '_chipList && _chipList.required || null',
},
})
export class NovoChipInput implements NovoChipTextControl, OnChanges {
export class NovoChipInput implements NovoChipTextControl, OnChanges, OnDestroy {
/** Whether the control is focused. */
focused: boolean = false;

Expand Down Expand Up @@ -91,20 +93,28 @@ export class NovoChipInput implements NovoChipTextControl, OnChanges {
/** The native input element to which this directive is attached. */
protected _inputElement: HTMLInputElement;

destroy$ = new Subject<void>();

constructor(
protected _elementRef: ElementRef<HTMLInputElement>,
@Inject(NOVO_CHIPS_DEFAULT_OPTIONS) private _defaultOptions: NovoChipsDefaultOptions,
@Inject(forwardRef(() => NovoChipList)) private _chipList: NovoChipList,
protected ngControl: NgControl,
@Inject(NOVO_CHIPS_DEFAULT_OPTIONS) private readonly _defaultOptions: NovoChipsDefaultOptions,
@Optional() @Inject(NovoFieldElement) private readonly _field: NovoFieldElement,
@Inject(forwardRef(() => NovoChipList)) private readonly _chipList: NovoChipList,
@Optional() @Self() protected ngControl: NgControl,
) {
this._inputElement = this._elementRef.nativeElement as HTMLInputElement;
this._inputElement = this._elementRef.nativeElement;
this._chipList.registerInput(this);
}

ngOnChanges() {
this._chipList.stateChanges.next();
}

ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}

/** Utility method to make host definition/tests more clear. */
_keydown(event?: KeyboardEvent) {
// Allow the user's focus to escape when they're tabbing forward. Note that we don't
Expand All @@ -117,9 +127,11 @@ export class NovoChipInput implements NovoChipTextControl, OnChanges {
}

/** Checks to see if the blur should emit the (chipEnd) event. */
_blur() {
_blur(blurEvent: FocusEvent) {
if (this.addOnBlur) {
this._emitChipEnd();
} else if (!this._field.blurEventIsInField(blurEvent)) {
this.clearValue();
}
this.focused = false;
// Blur the chip list if it is not focused
Expand Down Expand Up @@ -161,14 +173,14 @@ export class NovoChipInput implements NovoChipTextControl, OnChanges {
/** Clears the input. */
clearValue(): void {
this._inputElement.value = '';
this.ngControl?.control.setValue('');
this.ngControl?.control?.setValue('');
}

/** Checks whether a keycode is one of the configured separators. */
private _isSeparatorKey(event: KeyboardEvent) {
return !hasModifierKey(event) && new Set(this.separatorKeyCodes).has(event.key);
}

static ngAcceptInputType_addOnBlur: BooleanInput;
static ngAcceptInputType_disabled: BooleanInput;
static readonly ngAcceptInputType_addOnBlur: BooleanInput;
static readonly ngAcceptInputType_disabled: BooleanInput;
}
14 changes: 0 additions & 14 deletions projects/novo-elements/src/elements/chips/ChipList.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,10 @@ $novo-chip-input-margin: 4px;

novo-field.novo-focused {
input.novo-chip-input {
opacity: 1;
width: $novo-chip-input-width;
margin: $novo-chip-input-margin;
flex: 1 0 $novo-chip-input-width;
}
}

novo-field:not(.novo-focused) {
.novo-field-input .novo-chip-list-has-value {
margin-right: 2rem;
input.novo-chip-input {
opacity: 0;
width: 0px !important;
max-width: 0px !important;
}
}
}

input.novo-chip-input {
width: 20px;
margin: $novo-chip-input-margin;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { InjectionToken } from '@angular/core';
import { NovoOverlayTemplateComponent } from '../overlay';
import { AbstractConstructor, Constructor } from './constructor';
import { CanDisable } from './disabled.mixin';
Expand All @@ -12,6 +13,8 @@ export interface HasOverlay {
togglePanel(): void;
}

export const NOVO_OVERLAY_CONTAINER = new InjectionToken<HasOverlay>('NovoOverlayContainer');

/** @docs-private */
export type HasOverlayCtor = Constructor<HasOverlay>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ describe('Elements: NovoDataTablePagination', () => {
component = fixture.debugElement.componentInstance;
}));

describe('Setter: set page()', () => {
beforeEach(() => {
component.pageSize = 25;
component.length = 500;
component.ngOnInit();
spyOn(component, 'updateDisplayedPageSizeOptions').and.callThrough();
});
it('should set displayed pages via updateDisplayedPageSizeOptions function', () => {
component.selectPage(10);
expect(component.page).toEqual(10);
const actualDisplayedPageNumbers1 = component.pages.map((page) => page.number);
expect(actualDisplayedPageNumbers1).toEqual([8, 9, 10, 11, 12]);
component.page = 1;
expect(component.page).toEqual(1);
const actualDisplayedPageNumbers2 = component.pages.map((page) => page.number);
expect(component.updateDisplayedPageSizeOptions).toHaveBeenCalled();
expect(actualDisplayedPageNumbers2).toEqual([1, 2, 3, 4, 5]);
});
});

describe('Method: selectPage()', () => {
beforeEach(() => {
spyOn(component.state, 'checkRetainment');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const MAX_PAGES_DISPLAYED = 5;
<i class="bhi-next" data-automation-id="pager-next"></i>
</li>
</ul>
<novo-loading *ngIf="loading"></novo-loading>
<novo-spinner *ngIf="loading"></novo-spinner>
<button *ngIf="errorLoading"
theme="primary"
color="negative"
Expand All @@ -114,6 +114,7 @@ export class NovoDataTablePagination<T> implements OnInit, OnDestroy {
this.longRangeLabel = this.labels.getRangeText(this.page, this.pageSize, this.length, false);
this.shortRangeLabel = this.labels.getRangeText(this.page, this.pageSize, this.length, true);
this.state.page = this._page;
this.updateDisplayedPageSizeOptions();
}
_page: number = 0;

Expand Down
6 changes: 6 additions & 0 deletions projects/novo-elements/src/elements/field/field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
}
}

&.ng-invalid.ng-dirty.ng-touched {
.novo-field-input {
border-bottom: 1px solid $negative !important;
}
}

.novo-field-input {
grid-area: input;
display: grid;
Expand Down
Loading

0 comments on commit 286c486

Please sign in to comment.