feat(upgrade): support multi-slot projection in upgrade/static (#14282)

Closes #14261
This commit is contained in:
Pete Bacon Darwin
2017-03-14 00:34:53 +00:00
committed by Chuck Jazdzewski
parent ab40fcb068
commit 914797a8ff
19 changed files with 1135 additions and 25 deletions

View File

@ -0,0 +1,96 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as angular from '@angular/upgrade/src/common/angular1';
import {DowngradeComponentAdapter} from '@angular/upgrade/src/common/downgrade_component_adapter';
import {NgContentSelectorHelper} from '@angular/upgrade/src/common/ng_content_selector_helper';
import {nodes} from './test_helpers';
export function main() {
describe('DowngradeComponentAdapter', () => {
describe('groupNodesBySelector', () => {
function createAdapter(selectors: string[], contentNodes: Node[]): DowngradeComponentAdapter {
const selectorHelper = new NgContentSelectorHelper();
const fakeInjector = {get: function() { return selectorHelper; }};
const fakeScope = { $new: function() {} } as any;
const element = angular.element('<div></div>');
element.append(contentNodes);
return new DowngradeComponentAdapter(
'id', {component: null, selectors}, element, null, fakeScope, null, fakeInjector, null,
null, null, null);
}
it('should return an array of node collections for each selector', () => {
const contentNodes = nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<input type="number" name="myNum">' +
'<input type="date" name="myDate">' +
'<span>span content</span>' +
'<div class="x"><span>div-2 content</span></div>');
const selectors = ['input[type=date]', 'span', '.x'];
const adapter = createAdapter(selectors, contentNodes);
const projectableNodes = adapter.groupProjectableNodes();
expect(projectableNodes[0]).toEqual(nodes('<input type="date" name="myDate">'));
expect(projectableNodes[1]).toEqual(nodes('<span>span content</span>'));
expect(projectableNodes[2])
.toEqual(nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<div class="x"><span>div-2 content</span></div>'));
});
it('should collect up unmatched nodes for the wildcard selector', () => {
const contentNodes = nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<input type="number" name="myNum">' +
'<input type="date" name="myDate">' +
'<span>span content</span>' +
'<div class="x"><span>div-2 content</span></div>');
const selectors = ['.x', '*', 'input[type=date]'];
const adapter = createAdapter(selectors, contentNodes);
const projectableNodes = adapter.groupProjectableNodes();
expect(projectableNodes[0])
.toEqual(nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<div class="x"><span>div-2 content</span></div>'));
expect(projectableNodes[1])
.toEqual(nodes(
'<input type="number" name="myNum">' +
'<span>span content</span>'));
expect(projectableNodes[2]).toEqual(nodes('<input type="date" name="myDate">'));
});
it('should return an array of empty arrays if there are no nodes passed in', () => {
const selectors = ['.x', '*', 'input[type=date]'];
const adapter = createAdapter(selectors, []);
const projectableNodes = adapter.groupProjectableNodes();
expect(projectableNodes).toEqual([[], [], []]);
});
it('should return an empty array for each selector that does not match', () => {
const contentNodes = nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<input type="number" name="myNum">' +
'<input type="date" name="myDate">' +
'<span>span content</span>' +
'<div class="x"><span>div-2 content</span></div>');
const adapter1 = createAdapter([], contentNodes);
const projectableNodes = adapter1.groupProjectableNodes();
expect(projectableNodes).toEqual([]);
const adapter2 = createAdapter(['.not-there'], contentNodes);
const noMatchSelectorNodes = adapter2.groupProjectableNodes();
expect(noMatchSelectorNodes).toEqual([[]]);
});
});
});
}

View File

@ -23,3 +23,9 @@ export function html(html: string): Element {
export function multiTrim(text: string): string {
return text.replace(/\n/g, '').replace(/\s\s+/g, ' ').trim();
}
export function nodes(html: string) {
const div = document.createElement('div');
div.innerHTML = html.trim();
return Array.prototype.slice.call(div.childNodes);
}

View File

@ -7,9 +7,3 @@
*/
export * from '../common/test_helpers';
export function nodes(html: string) {
const div = document.createElement('div');
div.innerHTML = html.trim();
return Array.prototype.slice.call(div.childNodes);
}

View File

@ -140,5 +140,39 @@ export function main() {
expect(document.body.textContent).toEqual('ng1(ng2(ng1(ng2-transclude)))');
});
}));
it('should support multi-slot projection', async(() => {
@Component({
selector: 'ng2',
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
'2b(<ng-content select=".ng1b"></ng-content>)'
})
class Ng2Component {
constructor() {}
}
@NgModule({
declarations: [Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
const ng1Module = angular.module('ng1', []).directive(
'ng2', downgradeComponent({component: Ng2Component, selectors: ['.ng1a', '.ng1b']}));
// The ng-if on one of the projected children is here to make sure
// the correct slot is targeted even with structural directives in play.
const element = html(
'<ng2><div ng-if="true" class="ng1a">1a</div><div' +
' class="ng1b">1b</div></ng2>');
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
expect(document.body.textContent).toEqual('2a(1a)2b(1b)');
});
}));
});
}