Skip to content

Commit d3ba6ab

Browse files
tibing-old-emailnnixaa
authored andcommitted
refactor(theme): refactor popover, context-menu and search components 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.
1 parent 3211c54 commit d3ba6ab

40 files changed

+609
-1621
lines changed

e2e/context-menu.e2e-spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { browser, by, element } from 'protractor';
22

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

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

e2e/layout-dynamic.e2e-spec.ts

Lines changed: 0 additions & 90 deletions
This file was deleted.

e2e/popover.e2e-spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const placementLeft = by.css('nb-card:nth-child(2) button:nth-child(4)');
1111
const modeClick = by.css('nb-card:nth-child(4) button:nth-child(1)');
1212
const modeHover = by.css('nb-card:nth-child(4) button:nth-child(2)');
1313
const modeHint = by.css('nb-card:nth-child(4) button:nth-child(3)');
14-
const popover = by.css('nb-layout > nb-popover');
14+
const popover = by.css('nb-layout nb-popover');
1515

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

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

@@ -49,28 +49,28 @@ describe('nb-popover', () => {
4949
element(placementRight).click();
5050
const container = element(popover);
5151
expect(container.isPresent()).toBeTruthy();
52-
expect(container.getAttribute('class')).toEqual('right');
52+
expect(container.getAttribute('class')).toEqual('nb-overlay-right');
5353
});
5454

5555
it('render container in the bottom', () => {
5656
element(placementBottom).click();
5757
const container = element(popover);
5858
expect(container.isPresent()).toBeTruthy();
59-
expect(container.getAttribute('class')).toEqual('bottom');
59+
expect(container.getAttribute('class')).toEqual('nb-overlay-bottom');
6060
});
6161

6262
it('render container in the top', () => {
6363
element(placementTop).click();
6464
const container = element(popover);
6565
expect(container.isPresent()).toBeTruthy();
66-
expect(container.getAttribute('class')).toEqual('top');
66+
expect(container.getAttribute('class')).toEqual('nb-overlay-top');
6767
});
6868

6969
it('render container in the left', () => {
7070
element(placementLeft).click();
7171
const container = element(popover);
7272
expect(container.isPresent()).toBeTruthy();
73-
expect(container.getAttribute('class')).toEqual('left');
73+
expect(container.getAttribute('class')).toEqual('nb-overlay-left');
7474
});
7575

7676
it('open popover by host click', () => {

e2e/search.e2e-spec.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { hasClass } from './e2e-helper';
99
import { protractor } from 'protractor/built/ptor';
1010

1111
const EC = protractor.ExpectedConditions;
12+
const WAIT_TIME = 500;
1213

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

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

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

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

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

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

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

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

5280
it('should clean search input when closed', () => {
5381
element(by.css('.start-search')).click();
82+
// TODO: Remove after implementing search animations with angular.
83+
// For now need to wait animation to complete before performing checks.
84+
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
5485
element(by.css('.search-input')).sendKeys('akveo');
5586
element(by.css('.search button')).click();
87+
// TODO: Remove after implementing search animations with angular.
88+
// For now need to wait animation to complete before performing checks.
89+
browser.wait(EC.visibilityOf(element(by.css('.start-search'))), WAIT_TIME);
5690
expect(element(by.css('.search-input')).getAttribute('value')).toEqual('');
5791
});
5892

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

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

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

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

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

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

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

110165
it('should display customised placeholder', () => {
111166
element(by.css('.start-search')).click();
167+
// TODO: Remove after implementing search animations with angular.
168+
// For now need to wait animation to complete before performing checks.
169+
browser.wait(EC.visibilityOf(element(by.css('.search-input'))), WAIT_TIME);
112170
expect(element(by.css('.search-input')).getAttribute('placeholder')).toEqual('Type here.');
113171
});
114172
});

src/framework/theme/components/cdk/adapter/adapter.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import { NbViewportRulerAdapter } from './viewport-ruler-adapter';
99
@NgModule({
1010
providers: [
1111
NbViewportRulerAdapter,
12-
{ provide: OverlayContainer, useClass: NbOverlayContainerAdapter },
12+
NbOverlayContainerAdapter,
13+
{ provide: OverlayContainer, useExisting: NbOverlayContainerAdapter },
1314
{ provide: ScrollDispatcher, useClass: NbScrollDispatcherAdapter },
1415
],
1516
})

src/framework/theme/components/cdk/adapter/overlay-container-adapter.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,31 @@ import { Injectable } from '@angular/core';
33
import { NbOverlayContainer } from '../overlay/mapping';
44

55

6+
/**
7+
* Provides nb-layout as overlay container.
8+
* Container has to be cleared when layout destroys.
9+
* Another way previous version of the container will be used
10+
* but it isn't inserted in DOM and exists in memory only.
11+
* This case important only if you switch between multiple layouts.
12+
* */
613
@Injectable()
714
export class NbOverlayContainerAdapter extends NbOverlayContainer {
15+
protected container: HTMLElement;
16+
17+
setContainer(container: HTMLElement) {
18+
this.container = container;
19+
}
20+
21+
clearContainer() {
22+
this.container = null;
23+
this._containerElement = null;
24+
}
25+
826
protected _createContainer(): void {
927
const container = this._document.createElement('div');
1028

1129
container.classList.add('cdk-overlay-container');
12-
this._document.querySelector('nb-layout').appendChild(container);
30+
this.container.appendChild(container);
1331
this._containerElement = container;
1432
}
1533
}

src/framework/theme/components/cdk/overlay/overlay-trigger.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export enum NbTrigger {
1414
* Each stream provides different events depends on implementation.
1515
* We have three main trigger strategies: click, hint and hover.
1616
* */
17+
/**
18+
* TODO maybe we have to use renderer.listen instead of observableFromEvent?
19+
* Renderer provides capability use it in service worker, ssr and so on.
20+
* */
1721
export abstract class NbTriggerStrategy {
1822
abstract show$: Observable<Event>;
1923
abstract hide$: Observable<Event>;

0 commit comments

Comments
 (0)