Skip to content

Commit

Permalink
refactor(theme): refactor popover, context-menu and search components…
Browse files Browse the repository at this point in the history
… to use overlays (#684)

Refactor popover, context-menu, and search, implement with overlays.
Remove capability add something to layout top. All dynamic layout components have to work with overlays now.

BREAKING CHANGE:
`appendToLayoutTop` and `clearLayoutTop` methods was removed from `NbThemeService`. Instead of this methods, you have to use `NbOverlayService`. It's the extension of @angular/cdk overlays, so, check [documentation](https://material.angular.io/cdk/overlay/overview) first of all. 

Basic usage of overlays may look like this:

```ts
constructor(protected overlay: NbOverlayService) {
}

const overlayRef = overlay.create();
const overlayComponentPortal = new ComponentPortal(MyOverlayComponent);
overlayRef.attach(overlayComponentPortal);
```

Closes #683, closes  #664, closes #668.
  • Loading branch information
tibing-old-email authored and nnixaa committed Sep 10, 2018
1 parent 3211c54 commit d3ba6ab
Show file tree
Hide file tree
Showing 40 changed files with 609 additions and 1,621 deletions.
2 changes: 1 addition & 1 deletion e2e/context-menu.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { browser, by, element } from 'protractor';

const withContextMenu = by.css('nb-card:nth-child(1) nb-user:nth-child(1)');
const popover = by.css('nb-layout > nb-popover');
const popover = by.css('nb-layout nb-context-menu');

describe('nb-context-menu', () => {

Expand Down
90 changes: 0 additions & 90 deletions e2e/layout-dynamic.e2e-spec.ts

This file was deleted.

12 changes: 6 additions & 6 deletions e2e/popover.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const placementLeft = by.css('nb-card:nth-child(2) button:nth-child(4)');
const modeClick = by.css('nb-card:nth-child(4) button:nth-child(1)');
const modeHover = by.css('nb-card:nth-child(4) button:nth-child(2)');
const modeHint = by.css('nb-card:nth-child(4) button:nth-child(3)');
const popover = by.css('nb-layout > nb-popover');
const popover = by.css('nb-layout nb-popover');

describe('nb-popover', () => {

Expand All @@ -35,7 +35,7 @@ describe('nb-popover', () => {
element(contentString).click();
const containerContent = element(popover).element(by.css('div'));
expect(containerContent.isPresent()).toBeTruthy();
expect(containerContent.getAttribute('class')).toEqual('primitive-popover');
expect(containerContent.getAttribute('class')).toEqual('primitive-overlay');
expect(containerContent.getText()).toEqual('Hi, I\'m popover!');
});

Expand All @@ -49,28 +49,28 @@ describe('nb-popover', () => {
element(placementRight).click();
const container = element(popover);
expect(container.isPresent()).toBeTruthy();
expect(container.getAttribute('class')).toEqual('right');
expect(container.getAttribute('class')).toEqual('nb-overlay-right');
});

it('render container in the bottom', () => {
element(placementBottom).click();
const container = element(popover);
expect(container.isPresent()).toBeTruthy();
expect(container.getAttribute('class')).toEqual('bottom');
expect(container.getAttribute('class')).toEqual('nb-overlay-bottom');
});

it('render container in the top', () => {
element(placementTop).click();
const container = element(popover);
expect(container.isPresent()).toBeTruthy();
expect(container.getAttribute('class')).toEqual('top');
expect(container.getAttribute('class')).toEqual('nb-overlay-top');
});

it('render container in the left', () => {
element(placementLeft).click();
const container = element(popover);
expect(container.isPresent()).toBeTruthy();
expect(container.getAttribute('class')).toEqual('left');
expect(container.getAttribute('class')).toEqual('nb-overlay-left');
});

it('open popover by host click', () => {
Expand Down
58 changes: 58 additions & 0 deletions e2e/search.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { hasClass } from './e2e-helper';
import { protractor } from 'protractor/built/ptor';

const EC = protractor.ExpectedConditions;
const WAIT_TIME = 500;

describe('nb-search', () => {

Expand All @@ -18,47 +19,86 @@ describe('nb-search', () => {

it('should be able to show search-field', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
expect(hasClass(element(by.css('nb-search-field')), 'show')).toBeTruthy();
});

it('should be able to change layout style', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
expect(hasClass(element(by.css('nb-layout')), 'with-search')).toBeTruthy();
});

it('should focus on opened search field', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
expect(hasClass(browser.driver.switchTo().activeElement(), 'search-input')).toBeTruthy();
});

it('should be able to close search-field with close button', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
element(by.css('.search button')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.start-search'))), WAIT_TIME);
expect(hasClass(element(by.css('nb-search-field')), 'show')).toBeFalsy();
});

it('should remove class from layout when search closed', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
element(by.css('.search button')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.start-search'))), WAIT_TIME);
expect(hasClass(element(by.css('nb-layout')), 'with-search')).toBeFalsy();
});

it('should remove focus from input when search closed', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
element(by.css('.search button')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.start-search'))), WAIT_TIME);
expect(hasClass(browser.driver.switchTo().activeElement(), 'search-input')).toBeFalsy();
});

it('should clean search input when closed', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
element(by.css('.search-input')).sendKeys('akveo');
element(by.css('.search button')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.start-search'))), WAIT_TIME);
expect(element(by.css('.search-input')).getAttribute('value')).toEqual('');
});

it('should be able to close search-field with esc', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
element(by.css('.search-input')).sendKeys(protractor.Key.ESCAPE);
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.start-search'))), WAIT_TIME);
expect(hasClass(element(by.css('nb-search-field')), 'show')).toBeFalsy();
expect(hasClass(element(by.css('nb-layout')), 'with-search')).toBeFalsy();
expect(hasClass(browser.driver.switchTo().activeElement(), 'search-input')).toBeFalsy();
Expand All @@ -67,8 +107,14 @@ describe('nb-search', () => {

it('should be able to submit search and close search-field with enter', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
element(by.css('.search-input')).sendKeys('akveo');
element(by.css('.search-input')).sendKeys(protractor.Key.ENTER);
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.start-search'))), WAIT_TIME);
expect(hasClass(element(by.css('nb-search-field')), 'show')).toBeFalsy();
expect(hasClass(element(by.css('nb-layout')), 'with-search')).toBeFalsy();
expect(hasClass(browser.driver.switchTo().activeElement(), 'search-input')).toBeFalsy();
Expand All @@ -77,6 +123,9 @@ describe('nb-search', () => {

it('should display default hint', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
expect(element(by.css('.show .search span'))).toBeTruthy();

const spanEl = element(by.css('.show .search span'));
Expand All @@ -87,6 +136,9 @@ describe('nb-search', () => {

it('should display default placeholder', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
expect(element(by.css('.search-input')).getAttribute('placeholder')).toEqual('Search...');
});
});
Expand All @@ -99,6 +151,9 @@ describe('nb-search-customized', () => {

it('should display customised hint', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
expect(element(by.css('.show .search span'))).toBeTruthy();

const spanEl = element(by.css('.show .search span'));
Expand All @@ -109,6 +164,9 @@ describe('nb-search-customized', () => {

it('should display customised placeholder', () => {
element(by.css('.start-search')).click();
// TODO: Remove after implementing search animations with angular.
// For now need to wait animation to complete before performing checks.
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
expect(element(by.css('.search-input')).getAttribute('placeholder')).toEqual('Type here.');
});
});
3 changes: 2 additions & 1 deletion src/framework/theme/components/cdk/adapter/adapter.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { NbViewportRulerAdapter } from './viewport-ruler-adapter';
@NgModule({
providers: [
NbViewportRulerAdapter,
{ provide: OverlayContainer, useClass: NbOverlayContainerAdapter },
NbOverlayContainerAdapter,
{ provide: OverlayContainer, useExisting: NbOverlayContainerAdapter },
{ provide: ScrollDispatcher, useClass: NbScrollDispatcherAdapter },
],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,31 @@ import { Injectable } from '@angular/core';
import { NbOverlayContainer } from '../overlay/mapping';


/**
* Provides nb-layout as overlay container.
* Container has to be cleared when layout destroys.
* Another way previous version of the container will be used
* but it isn't inserted in DOM and exists in memory only.
* This case important only if you switch between multiple layouts.
* */
@Injectable()
export class NbOverlayContainerAdapter extends NbOverlayContainer {
protected container: HTMLElement;

setContainer(container: HTMLElement) {
this.container = container;
}

clearContainer() {
this.container = null;
this._containerElement = null;
}

protected _createContainer(): void {
const container = this._document.createElement('div');

container.classList.add('cdk-overlay-container');
this._document.querySelector('nb-layout').appendChild(container);
this.container.appendChild(container);
this._containerElement = container;
}
}
4 changes: 4 additions & 0 deletions src/framework/theme/components/cdk/overlay/overlay-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export enum NbTrigger {
* Each stream provides different events depends on implementation.
* We have three main trigger strategies: click, hint and hover.
* */
/**
* TODO maybe we have to use renderer.listen instead of observableFromEvent?
* Renderer provides capability use it in service worker, ssr and so on.
* */
export abstract class NbTriggerStrategy {
abstract show$: Observable<Event>;
abstract hide$: Observable<Event>;
Expand Down
Loading

0 comments on commit d3ba6ab

Please sign in to comment.