fix(aio): show embedded ToC (#23944)
On narrow screens (where there is not enough room on the right to show the floating ToC), an embedded ToC is shown (via an `<aio-toc embedded>` element in the document). Since ToC was not a custom element, the component was not instantiated for the embedded element. This commit fixes it by making `aio-toc` a custom element and loading it manually for the floating ToC (if necessary). PR Close #23944
This commit is contained in:
parent
431a42a238
commit
6e05ae02a2
|
@ -2,8 +2,8 @@
|
|||
"aio": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 2712,
|
||||
"main": 479729,
|
||||
"runtime": 2768,
|
||||
"main": 475857,
|
||||
"polyfills": 38453,
|
||||
"prettify": 14913
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
</mat-sidenav-container>
|
||||
|
||||
<div *ngIf="hasFloatingToc" class="toc-container no-print" [style.max-height.px]="tocMaxHeight" (mousewheel)="restrainScrolling($event)">
|
||||
<aio-toc></aio-toc>
|
||||
<aio-lazy-ce selector="aio-toc"></aio-lazy-ce>
|
||||
</div>
|
||||
|
||||
<footer class="no-print">
|
||||
|
|
|
@ -6,7 +6,7 @@ import { HttpClient } from '@angular/common/http';
|
|||
import { MatProgressBar, MatSidenav } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { timer } from 'rxjs';
|
||||
import { of, timer } from 'rxjs';
|
||||
import { first, mapTo } from 'rxjs/operators';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
@ -14,6 +14,7 @@ import { AppModule } from './app.module';
|
|||
import { DocumentService } from 'app/documents/document.service';
|
||||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { ElementsLoader } from 'app/custom-elements/elements-loader';
|
||||
import { GaService } from 'app/shared/ga.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
|
@ -26,7 +27,6 @@ import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
|||
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { SelectComponent } from 'app/shared/select/select.component';
|
||||
import { TocComponent } from 'app/layout/toc/toc.component';
|
||||
import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
|
||||
const sideBySideBreakPoint = 992;
|
||||
|
@ -621,53 +621,53 @@ describe('AppComponent', () => {
|
|||
});
|
||||
|
||||
describe('aio-toc', () => {
|
||||
let tocDebugElement: DebugElement;
|
||||
let tocContainer: DebugElement|null;
|
||||
let tocContainer: HTMLElement|null;
|
||||
let toc: HTMLElement|null;
|
||||
|
||||
const setHasFloatingToc = (hasFloatingToc: boolean) => {
|
||||
component.hasFloatingToc = hasFloatingToc;
|
||||
fixture.detectChanges();
|
||||
|
||||
tocDebugElement = fixture.debugElement.query(By.directive(TocComponent));
|
||||
tocContainer = tocDebugElement && tocDebugElement.parent;
|
||||
tocContainer = fixture.debugElement.nativeElement.querySelector('.toc-container');
|
||||
toc = tocContainer && tocContainer.querySelector('aio-toc');
|
||||
};
|
||||
|
||||
beforeEach(() => setHasFloatingToc(true));
|
||||
|
||||
|
||||
it('should show/hide `<aio-toc>` based on `hasFloatingToc`', () => {
|
||||
expect(tocDebugElement).toBeTruthy();
|
||||
expect(tocContainer).toBeTruthy();
|
||||
expect(toc).toBeTruthy();
|
||||
|
||||
setHasFloatingToc(false);
|
||||
expect(tocDebugElement).toBeFalsy();
|
||||
expect(tocContainer).toBeFalsy();
|
||||
expect(toc).toBeFalsy();
|
||||
|
||||
setHasFloatingToc(true);
|
||||
expect(tocDebugElement).toBeTruthy();
|
||||
expect(tocContainer).toBeTruthy();
|
||||
expect(toc).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have a non-embedded `<aio-toc>` element', () => {
|
||||
expect(tocDebugElement.classes['embedded']).toBeFalsy();
|
||||
expect(toc!.classList.contains('embedded')).toBe(false);
|
||||
});
|
||||
|
||||
it('should update the TOC container\'s `maxHeight` based on `tocMaxHeight`', () => {
|
||||
expect(tocContainer!.styles['max-height']).toBeNull();
|
||||
expect(tocContainer!.style['max-height']).toBe('');
|
||||
|
||||
component.tocMaxHeight = '100';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(tocContainer!.styles['max-height']).toBe('100px');
|
||||
expect(tocContainer!.style['max-height']).toBe('100px');
|
||||
});
|
||||
|
||||
it('should restrain scrolling inside the ToC container', () => {
|
||||
const restrainScrolling = spyOn(component, 'restrainScrolling');
|
||||
const evt = {};
|
||||
const evt = new MouseEvent('mousewheel');
|
||||
|
||||
expect(restrainScrolling).not.toHaveBeenCalled();
|
||||
|
||||
tocContainer!.triggerEventHandler('mousewheel', evt);
|
||||
tocContainer!.dispatchEvent(evt);
|
||||
expect(restrainScrolling).toHaveBeenCalledWith(evt);
|
||||
});
|
||||
});
|
||||
|
@ -1280,6 +1280,7 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
|
|||
imports: [ AppModule ],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
{ provide: ElementsLoader, useClass: TestElementsLoader },
|
||||
{ provide: GaService, useClass: TestGaService },
|
||||
{ provide: HttpClient, useClass: TestHttpClient },
|
||||
{ provide: LocationService, useFactory: () => mockLocationService },
|
||||
|
@ -1294,6 +1295,14 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
|
|||
});
|
||||
}
|
||||
|
||||
class TestElementsLoader {
|
||||
loadContainingCustomElements = jasmine.createSpy('loadContainingCustomElements')
|
||||
.and.returnValue(of(undefined));
|
||||
|
||||
loadCustomElement = jasmine.createSpy('loadCustomElement')
|
||||
.and.returnValue(Promise.resolve());
|
||||
}
|
||||
|
||||
class TestGaService {
|
||||
locationChanged = jasmine.createSpy('locationChanged');
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import { ScrollService } from 'app/shared/scroll.service';
|
|||
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { NotificationComponent } from 'app/layout/notification/notification.component';
|
||||
import { TocComponent } from 'app/layout/toc/toc.component';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
import { CurrentDateToken, currentDateProvider } from 'app/shared/current-date';
|
||||
import { WindowToken, windowProvider } from 'app/shared/window';
|
||||
|
@ -111,7 +110,6 @@ export const svgIconProviders = [
|
|||
NavItemComponent,
|
||||
SearchBoxComponent,
|
||||
NotificationComponent,
|
||||
TocComponent,
|
||||
TopMenuComponent,
|
||||
],
|
||||
providers: [
|
||||
|
@ -133,7 +131,6 @@ export const svgIconProviders = [
|
|||
{ provide: CurrentDateToken, useFactory: currentDateProvider },
|
||||
{ provide: WindowToken, useFactory: windowProvider },
|
||||
],
|
||||
entryComponents: [ TocComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
|
|
@ -24,6 +24,10 @@ export const ELEMENT_MODULE_PATHS_AS_ROUTES = [
|
|||
selector: 'aio-resource-list',
|
||||
loadChildren: './resource/resource-list.module#ResourceListModule'
|
||||
},
|
||||
{
|
||||
selector: 'aio-toc',
|
||||
loadChildren: './toc/toc.module#TocModule'
|
||||
},
|
||||
{
|
||||
selector: 'code-example',
|
||||
loadChildren: './code/code-example.module#CodeExampleModule'
|
||||
|
|
|
@ -4,8 +4,8 @@ import { By } from '@angular/platform-browser';
|
|||
import { asapScheduler as asap, BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { TocComponent } from './toc.component';
|
||||
import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
import { TocComponent } from './toc.component';
|
||||
|
||||
describe('TocComponent', () => {
|
||||
let tocComponentDe: DebugElement;
|
|
@ -0,0 +1,14 @@
|
|||
import { NgModule, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { WithCustomElementComponent } from '../element-registry';
|
||||
import { TocComponent } from './toc.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule, MatIconModule ],
|
||||
declarations: [ TocComponent ],
|
||||
entryComponents: [ TocComponent ],
|
||||
})
|
||||
export class TocModule implements WithCustomElementComponent {
|
||||
customElementComponent: Type<any> = TocComponent;
|
||||
}
|
Loading…
Reference in New Issue