Skip to content

Commit

Permalink
feat(context-menu, notification-panel): set trigger button as active …
Browse files Browse the repository at this point in the history
…(#96)
  • Loading branch information
Phil147 authored and GitHub Enterprise committed Nov 4, 2020
1 parent 38ca679 commit 6ffd738
Show file tree
Hide file tree
Showing 16 changed files with 147 additions and 64 deletions.
15 changes: 14 additions & 1 deletion projects/ng-aquila/src/button/button-base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ElementRef, ChangeDetectorRef, HostBinding, Directive } from '@angular/core';
import { NxTriggerButton } from '@aposin/ng-aquila/overlay';

/** Type of a button. */
export type NxButtonType = 'primary' | 'secondary' | 'tertiary' | 'cta' | 'emphasis';
Expand All @@ -11,7 +12,7 @@ const DEFAULT_TYPE = 'primary';

/** @docs-private */
@Directive()
export class NxButtonBase {
export class NxButtonBase implements NxTriggerButton {
private _classNames: string;

/** @docs-private */
Expand Down Expand Up @@ -50,6 +51,8 @@ export class NxButtonBase {
danger: boolean = false;
negative: boolean = false;
block: boolean = false;
@HostBinding('class.nx-button--active')
active: boolean = false;

constructor(private _changeDetectorRef: ChangeDetectorRef, private _elementRef: ElementRef) { }

Expand Down Expand Up @@ -87,4 +90,14 @@ export class NxButtonBase {
get elementRef() {
return this._elementRef;
}

setTriggerActive() {
this.active = true;
this._changeDetectorRef.markForCheck();
}

setTriggerInactive() {
this.active = false;
this._changeDetectorRef.markForCheck();
}
}
4 changes: 3 additions & 1 deletion projects/ng-aquila/src/button/button.component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NxTriggerButton } from '@aposin/ng-aquila/overlay';
import {
Component,
ElementRef,
Expand All @@ -12,7 +13,8 @@ import { NxButtonBase } from './button-base';
// tslint:disable-next-line:component-selector
selector: 'button[nxButton]',
changeDetection: ChangeDetectionStrategy.OnPush,
inputs: ['classNames:nxButton']
inputs: ['classNames:nxButton'],
providers: [{provide: NxTriggerButton, useExisting: NxButtonComponent}]
})

export class NxButtonComponent extends NxButtonBase {
Expand Down
8 changes: 4 additions & 4 deletions projects/ng-aquila/src/button/button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ $button-margin-bottom: nx-spacer(m);
}
}

&:active {
&:active, &.nx-button--active {
@include var(background-color, button-#{$type}-active-background-color);
@include var(color, button-#{$type}-active-text-color);
@include var(border-color, button-#{$type}-active-border-color);
Expand Down Expand Up @@ -114,7 +114,7 @@ $button-margin-bottom: nx-spacer(m);
}
}

&:active {
&:active, &.nx-button--active {
@include var(background-color, negative-02);
@include var(border-color, negative-02);
@include var(color, negative-accent);
Expand Down Expand Up @@ -154,7 +154,7 @@ $button-margin-bottom: nx-spacer(m);
}
}

&:active {
&:active, &.nx-button--active {
@include var(background-color, negative-02);
@include var(border-color, negative-02);
@include var(color, button-secondary-text-color);
Expand Down Expand Up @@ -191,7 +191,7 @@ $button-margin-bottom: nx-spacer(m);
}
}

&:active {
&:active, &.nx-button--active {
@include var(background-color, negative-02);
@include var(color, button-secondary-text-color);
}
Expand Down
4 changes: 3 additions & 1 deletion projects/ng-aquila/src/button/icon-button.component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NxTriggerButton } from '@aposin/ng-aquila/overlay';
import {
Component,
Input,
Expand All @@ -13,7 +14,8 @@ import { NxButtonBase } from './button-base';
templateUrl: './button.html',
styleUrls: ['button.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
inputs: ['classNames:nxIconButton']
inputs: ['classNames:nxIconButton'],
providers: [{provide: NxTriggerButton, useExisting: NxIconButtonComponent}]
})
export class NxIconButtonComponent extends NxButtonBase {
constructor(changeDetectorRef: ChangeDetectorRef, elementRef: ElementRef) {
Expand Down
4 changes: 2 additions & 2 deletions projects/ng-aquila/src/button/plain-button.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
}
}

&:active {
&:active, &.nx-button--active {
@include var(color, plain-button-active-color);
}

Expand Down Expand Up @@ -58,7 +58,7 @@
}
}

&:active {
&:active, &.nx-button--active {
@include var(color, plain-button-danger-active-color);
}

Expand Down
4 changes: 3 additions & 1 deletion projects/ng-aquila/src/button/plain-button.component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NxTriggerButton } from '@aposin/ng-aquila/overlay';
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';

@Component({
Expand All @@ -9,7 +10,8 @@ import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/
inputs: ['classNames:nxPlainButton'],
host: {
'[class.nx-plain-button--danger]': 'danger'
}
},
providers: [{provide: NxTriggerButton, useExisting: NxPlainButtonComponent}]
})
export class NxPlainButtonComponent {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Direction, Directionality } from '@angular/cdk/bidi';
import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
import {
ConnectedPosition,
FlexibleConnectedPositionStrategy,
Overlay,
OverlayConfig,
OverlayRef,
ScrollStrategy,
ConnectedPosition
} from '@angular/cdk/overlay';
import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
import { TemplatePortal } from '@angular/cdk/portal';
import {
AfterContentInit,
Expand All @@ -19,14 +20,15 @@ import {
Optional,
Output,
Self,
ViewContainerRef
ViewContainerRef,
} from '@angular/core';
import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
import { asapScheduler, merge, of as observableOf, Subscription, fromEvent, Observable } from 'rxjs';
import { delay, filter, take, takeUntil, map } from 'rxjs/operators';
import { NxContextMenuComponent } from './context-menu.component';
import { NxTriggerButton } from '@aposin/ng-aquila/overlay';
import { asapScheduler, fromEvent, merge, Observable, of as observableOf, Subscription } from 'rxjs';
import { delay, filter, map, take, takeUntil } from 'rxjs/operators';

import { throwNxContextMenuMissingError } from './context-menu-errors';
import { NxContextMenuItemComponent } from './context-menu-item.component';
import { NxContextMenuComponent } from './context-menu.component';

/** Default top padding of the menu panel. */
export const MENU_PANEL_TOP_PADDING = 16;
Expand Down Expand Up @@ -130,7 +132,8 @@ export class NxContextMenuTriggerDirective
@Optional()
@Self()
private _contextMenuItemInstance: NxContextMenuItemComponent,
@Optional() private _dir: Directionality) {
@Optional() private _dir: Directionality,
@Optional() @Self() private _triggerButton: NxTriggerButton) {

if (_contextMenuItemInstance) {
_contextMenuItemInstance._triggersSubmenu = this.triggersSubmenu();
Expand Down Expand Up @@ -197,6 +200,11 @@ export class NxContextMenuTriggerDirective
this.contextMenu._startAnimation();
}

if (this._triggerButton) {
this._triggerButton.setTriggerActive();
this.contextMenu.closed.pipe(take(1)).subscribe(() => this._triggerButton.setTriggerInactive());
}

this._waitForClose();
}

Expand Down
95 changes: 56 additions & 39 deletions projects/ng-aquila/src/context-menu/context-menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
createMouseEvent,
createKeyboardEvent
} from '../cdk-test-utils';
import { NxButtonComponent, NxButtonModule } from '@aposin/ng-aquila/button';

// For better readablity here, We can safely ignore some conventions in our specs
// tslint:disable:component-class-suffix
Expand Down Expand Up @@ -65,7 +66,7 @@ describe('nxContextMenu', () => {

function createComponent<T>(component: Type<T>, providers = []): ComponentFixture<T> {
TestBed.configureTestingModule({
imports: [NxContextMenuModule, NoopAnimationsModule, NxIconModule],
imports: [NxContextMenuModule, NoopAnimationsModule, NxIconModule, NxButtonModule],
declarations: [component],
providers
}).compileComponents();
Expand Down Expand Up @@ -478,6 +479,19 @@ describe('nxContextMenu', () => {
flush();
}));

it('should set trigger button active state', () => {
const fixture = createComponent(SimpleMenu);
fixture.detectChanges();

fixture.componentInstance.trigger.openContextMenu();
fixture.detectChanges();
expect(fixture.componentInstance.button.active).toBe(true);

fixture.componentInstance.trigger.closeContextMenu();
fixture.detectChanges();
expect(fixture.componentInstance.button.active).toBe(false);
});

describe('lazy rendering', () => {
it('should be able to render the menu content lazily', fakeAsync(() => {
const fixture = createComponent(SimpleLazyMenu);
Expand Down Expand Up @@ -1156,6 +1170,7 @@ describe('nxContextMenu', () => {
tick(500);

expect(overlay.querySelectorAll('.nx-context-menu').length).toBe(0, 'Expected no open menus');
expect(instance.rootButtonEl.active).toBe(false);
}));

it('should add an expand icon to the menu items that trigger a sub-menu', fakeAsync(() => {
Expand Down Expand Up @@ -1295,55 +1310,55 @@ describe('nxContextMenu', () => {
}));

it('should be able to open a submenu through an item that is not a direct descendant of the panel', fakeAsync(() => {
const nestedFixture = createComponent(SubmenuDeclaredInsideParentMenu);
overlay = overlayContainerElement;
const nestedFixture = createComponent(SubmenuDeclaredInsideParentMenu);
overlay = overlayContainerElement;

nestedFixture.detectChanges();
nestedFixture.componentInstance.rootTriggerEl.nativeElement.click();
nestedFixture.detectChanges();
tick(500);
expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(1, 'Expected one open menu');
nestedFixture.detectChanges();
nestedFixture.componentInstance.rootTriggerEl.nativeElement.click();
nestedFixture.detectChanges();
tick(500);
expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(1, 'Expected one open menu');

dispatchMouseEvent(overlay.querySelector('.level-one-trigger'), 'mouseenter');
nestedFixture.detectChanges();
tick(500);
dispatchMouseEvent(overlay.querySelector('.level-one-trigger'), 'mouseenter');
nestedFixture.detectChanges();
tick(500);

expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(2, 'Expected two open menus');
}));
expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(2, 'Expected two open menus');
}));

it('should not close when hovering over a menu item inside a sub-menu panel that is declared inside the root menu', fakeAsync(() => {
const nestedFixture = createComponent(SubmenuDeclaredInsideParentMenu);
overlay = overlayContainerElement;
const nestedFixture = createComponent(SubmenuDeclaredInsideParentMenu);
overlay = overlayContainerElement;

nestedFixture.detectChanges();
nestedFixture.componentInstance.rootTriggerEl.nativeElement.click();
nestedFixture.detectChanges();
tick(500);
expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(1, 'Expected one open menu');
nestedFixture.detectChanges();
nestedFixture.componentInstance.rootTriggerEl.nativeElement.click();
nestedFixture.detectChanges();
tick(500);
expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(1, 'Expected one open menu');

dispatchMouseEvent(overlay.querySelector('.level-one-trigger'), 'mouseenter');
nestedFixture.detectChanges();
tick(500);
dispatchMouseEvent(overlay.querySelector('.level-one-trigger'), 'mouseenter');
nestedFixture.detectChanges();
tick(500);

expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(2, 'Expected two open menus');
expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(2, 'Expected two open menus');

dispatchMouseEvent(overlay.querySelector('.level-two-item'), 'mouseenter');
nestedFixture.detectChanges();
tick(500);
dispatchMouseEvent(overlay.querySelector('.level-two-item'), 'mouseenter');
nestedFixture.detectChanges();
tick(500);

expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(2, 'Expected two open menus to remain');
}));
expect(overlay.querySelectorAll('.nx-context-menu').length)
.toBe(2, 'Expected two open menus to remain');
}));
});
});

@Component({
template: `
<button
<button nxButton="tertiary small"
[nxContextMenuTriggerFor]="menu"
#triggerEl>Toggle menu</button>
<nx-context-menu
Expand All @@ -1363,7 +1378,8 @@ describe('nxContextMenu', () => {
})
class SimpleMenu {
@ViewChild(NxContextMenuTriggerDirective) trigger: NxContextMenuTriggerDirective;
@ViewChild('triggerEl') triggerEl: ElementRef<HTMLElement>;
@ViewChild('triggerEl', {read: ElementRef}) triggerEl: ElementRef<HTMLElement>;
@ViewChild(NxButtonComponent) button: NxButtonComponent;
@ViewChild(NxContextMenuComponent) menu: NxContextMenuComponent;
@ViewChildren(NxContextMenuItemComponent) items: QueryList<NxContextMenuItemComponent>;
extraItems: string[] = [];
Expand All @@ -1372,10 +1388,10 @@ class SimpleMenu {

@Component({
template: `
<button
<button nxButton="tertiary small"
[nxContextMenuTriggerFor]="root"
#rootTrigger="nxContextMenuTrigger"
#rootTriggerEl>Toggle menu</button>
#rootTriggerEl #rootTriggerButton>Toggle menu</button>
<button
[nxContextMenuTriggerFor]="levelTwo"
Expand Down Expand Up @@ -1419,7 +1435,8 @@ class SimpleMenu {
class NestedMenu {
@ViewChild('root') rootMenu: NxContextMenuComponent;
@ViewChild('rootTrigger') rootTrigger: NxContextMenuTriggerDirective;
@ViewChild('rootTriggerEl') rootTriggerEl: ElementRef<HTMLElement>;
@ViewChild('rootTriggerEl', { read: ElementRef }) rootTriggerEl: ElementRef<HTMLElement>;
@ViewChild('rootTriggerEl', { read: NxButtonComponent }) rootButtonEl: NxButtonComponent;
@ViewChild('alternateTrigger') alternateTrigger: NxContextMenuTriggerDirective;
readonly rootCloseCallback = jasmine.createSpy('root menu closed callback');

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Directive, ElementRef, Input, TemplateRef } from '@angular/core';
import { NxOverlayConfig, NxOverlayRef, NxOverlayService } from '@aposin/ng-aquila/overlay';
import { Directive, ElementRef, Input, Optional, Self, TemplateRef } from '@angular/core';
import { NxOverlayConfig, NxOverlayRef, NxOverlayService, NxTriggerButton } from '@aposin/ng-aquila/overlay';
import { take } from 'rxjs/operators';

const DEFAULT_CONFIG: NxOverlayConfig = {
Expand Down Expand Up @@ -30,13 +30,15 @@ export class NxNotificationPanelTriggerDirective {

constructor(
private _nxOverlay: NxOverlayService,
private _element: ElementRef<HTMLElement>) { }
private _element: ElementRef<HTMLElement>,
@Optional() @Self() private _triggerButton: NxTriggerButton) { }

open() {
if (this._overlayRef) {
return;
}
this._overlayRef = this._nxOverlay.open(this._panelTemplate, this._element, DEFAULT_CONFIG);
const config = { ...DEFAULT_CONFIG, ...{ triggerButton: this._triggerButton }};
this._overlayRef = this._nxOverlay.open(this._panelTemplate, this._element, config);
this._overlayRef.afterClosed().pipe(take(1)).subscribe(() => this.close());
}

Expand Down
Loading

0 comments on commit 6ffd738

Please sign in to comment.