Files
Matthieu Riegler df9eed4ff1 refactor(core): remove ComponentFactoryResolver usages
This API was deprecated for a longtime, and was remove by #68055 from the public API.

We do have alternatives to this old API, so we can entirely remove it to spare some bytes
2026-04-13 16:00:03 +03:00

162 lines
4.9 KiB
TypeScript

/**
* @license
* Copyright Google LLC 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.dev/license
*/
import {
Component,
destroyPlatform,
EventEmitter,
Input,
NgModule,
Output,
reflectComponentType,
ViewEncapsulation,
} from '@angular/core';
import {BrowserModule, platformBrowser} from '@angular/platform-browser';
import {createCustomElement, NgElement} from '../src/create-custom-element';
describe('slots', () => {
let testContainer: HTMLDivElement;
beforeAll((done) => {
testContainer = document.createElement('div');
document.body.appendChild(testContainer);
destroyPlatform();
platformBrowser()
.bootstrapModule(TestModule)
.then((ref) => {
const injector = ref.injector;
testElements.forEach((comp) => {
const compType = reflectComponentType(comp)!;
customElements.define(compType.selector, createCustomElement(comp, {injector}));
});
})
.then(done, done.fail);
});
afterAll(() => {
destroyPlatform();
testContainer.remove();
(testContainer as any) = null;
});
it('should use slots to project content', () => {
const tpl = `<default-slot-el><span class="projected"></span></default-slot-el>`;
testContainer.innerHTML = tpl;
const testEl = testContainer.querySelector('default-slot-el')!;
const content = testContainer.querySelector('span.projected')!;
const slot = testEl.shadowRoot!.querySelector('slot')!;
const assignedNodes = slot.assignedNodes();
expect(assignedNodes[0]).toBe(content);
});
it('should use a named slot to project content', () => {
const tpl = `<named-slot-el><span class="projected" slot="header"></span></named-slot-el>`;
testContainer.innerHTML = tpl;
const testEl = testContainer.querySelector('named-slot-el')!;
const content = testContainer.querySelector('span.projected')!;
const slot = testEl.shadowRoot!.querySelector('slot[name=header]') as HTMLSlotElement;
const assignedNodes = slot.assignedNodes();
expect(assignedNodes[0]).toBe(content);
});
it('should use named slots to project content', () => {
const tpl = `
<named-slots-el>
<span class="projected-header" slot="header"></span>
<span class="projected-body" slot="body"></span>
</named-slots-el>`;
testContainer.innerHTML = tpl;
const testEl = testContainer.querySelector('named-slots-el')!;
const headerContent = testContainer.querySelector('span.projected-header')!;
const bodyContent = testContainer.querySelector('span.projected-body')!;
const headerSlot = testEl.shadowRoot!.querySelector('slot[name=header]') as HTMLSlotElement;
const bodySlot = testEl.shadowRoot!.querySelector('slot[name=body]') as HTMLSlotElement;
expect(headerContent.assignedSlot).toBe(headerSlot);
expect(bodyContent.assignedSlot).toBe(bodySlot);
});
it('should listen to slotchange events', (done) => {
const templateEl = document.createElement('template');
const tpl = `
<slot-events-el>
<span class="projected">Content</span>
</slot-events-el>`;
templateEl.innerHTML = tpl;
const template = templateEl.content.cloneNode(true) as DocumentFragment;
const testEl = template.querySelector('slot-events-el')! as NgElement & SlotEventsComponent;
testEl.addEventListener('slotEventsChange', (e) => {
expect(testEl.slotEvents.length).toEqual(1);
done();
});
testContainer.appendChild(template);
expect(testEl.slotEvents.length).toEqual(0);
});
});
// Helpers
@Component({
selector: 'default-slot-el',
template: '<div class="slotparent"><slot></slot></div>',
encapsulation: ViewEncapsulation.ShadowDom,
standalone: false,
})
class DefaultSlotComponent {
constructor() {}
}
@Component({
selector: 'named-slot-el',
template: '<div class="slotparent"><slot name="header"></slot></div>',
encapsulation: ViewEncapsulation.ShadowDom,
standalone: false,
})
class NamedSlotComponent {
constructor() {}
}
@Component({
selector: 'named-slots-el',
template: '<div class="slotparent"><slot name="header"></slot><slot name="body"></slot></div>',
encapsulation: ViewEncapsulation.ShadowDom,
standalone: false,
})
class NamedSlotsComponent {
constructor() {}
}
@Component({
selector: 'slot-events-el',
template: '<slot (slotchange)="onSlotChange($event)"></slot>',
encapsulation: ViewEncapsulation.ShadowDom,
standalone: false,
})
class SlotEventsComponent {
@Input() slotEvents: Event[] = [];
@Output() slotEventsChange = new EventEmitter();
constructor() {}
onSlotChange(event: Event) {
this.slotEvents.push(event);
this.slotEventsChange.emit(event);
}
}
const testElements = [
DefaultSlotComponent,
NamedSlotComponent,
NamedSlotsComponent,
SlotEventsComponent,
];
@NgModule({imports: [BrowserModule], declarations: testElements})
class TestModule {
ngDoBootstrap() {}
}