refactor(docs-infra): migrate adev to signal-based queries (#58646)

Used migration schametic to migrate to signal based queries and little manual migration where setter is used

PR Close #58646
This commit is contained in:
Sheik Althaf 2024-11-14 10:01:03 +05:30 committed by Jessica Janiuk
parent b972249145
commit f15f41111b
10 changed files with 76 additions and 79 deletions

View file

@ -21,7 +21,7 @@ import {
Input,
signal,
Type,
ViewChild,
viewChild,
} from '@angular/core';
import {CommonModule, DOCUMENT} from '@angular/common';
import {MatTabGroup, MatTabsModule} from '@angular/material/tabs';
@ -54,7 +54,7 @@ export class ExampleViewer {
@Input() githubUrl: string | null = null;
@Input() stackblitzUrl: string | null = null;
@ViewChild('codeTabs') matTabGroup?: MatTabGroup;
readonly matTabGroup = viewChild<MatTabGroup>('codeTabs');
private readonly changeDetector = inject(ChangeDetectorRef);
private readonly clipboard = inject(Clipboard);
@ -111,7 +111,7 @@ export class ExampleViewer {
`example-${this.exampleMetadata()?.id.toString()!}`,
);
this.matTabGroup?.realignInkBar();
this.matTabGroup()?.realignInkBar();
this.listenToMatTabIndexChange();
@ -142,8 +142,9 @@ export class ExampleViewer {
}
private listenToMatTabIndexChange(): void {
this.matTabGroup?.realignInkBar();
this.matTabGroup?.selectedIndexChange
const matTabGroup = this.matTabGroup();
matTabGroup?.realignInkBar();
matTabGroup?.selectedIndexChange
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((index) => {
this.snippetCode.set(this.exampleMetadata()?.files[index]);

View file

@ -29,8 +29,7 @@ describe('ProgressBarComponent', () => {
// We suspect a racing condition inside the RouterTestingHarness.
// Until this has been investigated, we will skip this test.
xit('should call progressBar.complete() on route change', async () => {
const progressBar = component.progressBar();
const progressBarCompleteSpy = spyOn(progressBar, 'complete');
const progressBarCompleteSpy = spyOn(component.progressBar(), 'complete');
const harness = await RouterTestingHarness.create();
await harness.navigateByUrl('/');

View file

@ -12,7 +12,6 @@ import {
inject,
OnInit,
PLATFORM_ID,
Signal,
viewChild,
} from '@angular/core';
import {isPlatformBrowser} from '@angular/common';
@ -41,7 +40,7 @@ export const PROGRESS_BAR_DELAY = 30;
export class ProgressBarComponent implements OnInit {
private readonly router = inject(Router);
progressBar = viewChild.required(NgProgressRef);
readonly progressBar = viewChild.required(NgProgressRef);
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));

View file

@ -97,7 +97,7 @@ describe('CodeEditor', () => {
component.ngAfterViewInit();
expect(codeMirrorEditorInitSpy).toHaveBeenCalledWith(
component['codeEditorWrapperRef'].nativeElement,
component.codeEditorWrapperRef().nativeElement,
);
});

View file

@ -15,9 +15,10 @@ import {
ElementRef,
EnvironmentInjector,
OnDestroy,
ViewChild,
afterRenderEffect,
inject,
signal,
viewChild,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {MatTabGroup, MatTabsModule} from '@angular/material/tabs';
@ -60,28 +61,13 @@ const ANGULAR_DEV = 'https://angular.dev';
],
})
export class CodeEditor implements AfterViewInit, OnDestroy {
@ViewChild('codeEditorWrapper') private codeEditorWrapperRef!: ElementRef<HTMLDivElement>;
@ViewChild(MatTabGroup) private matTabGroup!: MatTabGroup;
readonly codeEditorWrapperRef =
viewChild.required<ElementRef<HTMLDivElement>>('codeEditorWrapper');
readonly matTabGroup = viewChild.required(MatTabGroup);
private createFileInputRef?: ElementRef<HTMLInputElement>;
@ViewChild('createFileInput') protected set setFileInputRef(
element: ElementRef<HTMLInputElement>,
) {
if (element) {
element.nativeElement.focus();
this.createFileInputRef = element;
}
}
readonly createFileInputRef = viewChild<ElementRef<HTMLInputElement>>('createFileInput');
private renameFileInputRef?: ElementRef<HTMLInputElement>;
@ViewChild('renameFileInput') protected set setRenameFileInputRef(
element: ElementRef<HTMLInputElement>,
) {
if (element) {
element.nativeElement.focus();
this.renameFileInputRef = element;
}
}
readonly renameFileInputRef = viewChild<ElementRef<HTMLInputElement>>('renameFileInput');
private readonly destroyRef = inject(DestroyRef);
@ -117,8 +103,20 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
readonly isCreatingFile = signal<boolean>(false);
readonly isRenamingFile = signal<boolean>(false);
constructor() {
afterRenderEffect(() => {
const createFileInput = this.createFileInputRef();
createFileInput?.nativeElement.focus();
});
afterRenderEffect(() => {
const renameFileInput = this.renameFileInputRef();
renameFileInput?.nativeElement.focus();
});
}
ngAfterViewInit() {
this.codeMirrorEditor.init(this.codeEditorWrapperRef.nativeElement);
this.codeMirrorEditor.init(this.codeEditorWrapperRef().nativeElement);
this.listenToDiagnosticsChange();
this.listenToTabChange();
@ -164,12 +162,12 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
async deleteFile(filename: string) {
await this.codeMirrorEditor.deleteFile(filename);
this.matTabGroup.selectedIndex = 0;
this.matTabGroup().selectedIndex = 0;
}
onAddButtonClick() {
this.isCreatingFile.set(true);
this.matTabGroup.selectedIndex = this.files().length;
this.matTabGroup().selectedIndex = this.files().length;
}
onRenameButtonClick() {
@ -177,11 +175,12 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
}
async renameFile(event: SubmitEvent, oldPath: string) {
if (!this.renameFileInputRef) return;
const renameFileInput = this.renameFileInputRef();
if (!renameFileInput) return;
event.preventDefault();
const renameFileInputValue = this.renameFileInputRef.nativeElement.value;
const renameFileInputValue = renameFileInput.nativeElement.value;
if (renameFileInputValue) {
if (renameFileInputValue.includes('..')) {
@ -204,11 +203,12 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
}
async createFile(event: SubmitEvent) {
if (!this.createFileInputRef) return;
const fileInput = this.createFileInputRef();
if (!fileInput) return;
event.preventDefault();
const newFileInputValue = this.createFileInputRef.nativeElement.value;
const newFileInputValue = fileInput.nativeElement.value;
if (newFileInputValue) {
if (newFileInputValue.includes('..')) {
@ -248,13 +248,13 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
)
.subscribe(() => {
// selected file on project change is always the first
this.matTabGroup.selectedIndex = 0;
this.matTabGroup().selectedIndex = 0;
});
}
private listenToTabChange() {
this.matTabGroup.selectedIndexChange
.pipe(takeUntilDestroyed(this.destroyRef))
this.matTabGroup()
.selectedIndexChange.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((index) => {
const selectedFile = this.files()[index];

View file

@ -17,10 +17,10 @@ import {
OnDestroy,
OnInit,
PLATFORM_ID,
ViewChild,
computed,
inject,
signal,
viewChild,
} from '@angular/core';
import {takeUntilDestroyed, toObservable} from '@angular/core/rxjs-interop';
import {IconComponent} from '@angular/docs';
@ -53,8 +53,8 @@ export const LARGE_EDITOR_HEIGHT_BREAKPOINT = 550;
providers: [EditorUiState],
})
export class EmbeddedEditor implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('editorContainer') editorContainer!: ElementRef<HTMLDivElement>;
@ViewChild(MatTabGroup) matTabGroup!: MatTabGroup;
readonly editorContainer = viewChild.required<ElementRef<HTMLDivElement>>('editorContainer');
readonly matTabGroup = viewChild(MatTabGroup);
private readonly platformId = inject(PLATFORM_ID);
private readonly changeDetector = inject(ChangeDetectorRef);
@ -120,7 +120,10 @@ export class EmbeddedEditor implements OnInit, AfterViewInit, OnDestroy {
private setFirstTabAsActiveAfterResize(): void {
this.displayPreviewInMatTabGroup$.subscribe(() => {
this.changeDetector.detectChanges();
this.matTabGroup.selectedIndex = 0;
const matTabGroup = this.matTabGroup();
if (matTabGroup) {
matTabGroup.selectedIndex = 0;
}
});
}
@ -138,11 +141,11 @@ export class EmbeddedEditor implements OnInit, AfterViewInit, OnDestroy {
this.splitDirection = this.isLargeEmbeddedEditor() ? 'horizontal' : 'vertical';
});
this.resizeObserver.observe(this.editorContainer.nativeElement);
this.resizeObserver.observe(this.editorContainer().nativeElement);
}
private isLargeEmbeddedEditor(): boolean {
const editorContainer = this.editorContainer.nativeElement;
const editorContainer = this.editorContainer().nativeElement;
const width = editorContainer.offsetWidth;
const height = editorContainer.offsetHeight;

View file

@ -47,16 +47,4 @@ describe('Terminal', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should register the terminal element on afterViewInit', () => {
const terminalDebugElement = fixture.debugElement.query(By.css('.adev-terminal-output'));
component['terminalElementRef'] = terminalDebugElement;
component.ngAfterViewInit();
expect(terminalHandlerSpy.registerTerminal).toHaveBeenCalledWith(
TerminalType.READONLY,
terminalDebugElement.nativeElement,
);
});
});

View file

@ -13,9 +13,9 @@ import {
DestroyRef,
ElementRef,
Input,
ViewChild,
ViewEncapsulation,
inject,
viewChild,
} from '@angular/core';
import {debounceTime} from 'rxjs/operators';
@ -35,7 +35,7 @@ import {Subject} from 'rxjs';
})
export class Terminal implements AfterViewInit {
@Input({required: true}) type!: TerminalType;
@ViewChild('terminalOutput') private terminalElementRef!: ElementRef<HTMLElement>;
readonly terminalElementRef = viewChild.required<ElementRef<HTMLElement>>('terminalOutput');
private readonly destroyRef = inject(DestroyRef);
private readonly terminalHandler = inject(TerminalHandler);
@ -43,7 +43,7 @@ export class Terminal implements AfterViewInit {
private readonly resize$ = new Subject<void>();
ngAfterViewInit() {
this.terminalHandler.registerTerminal(this.type, this.terminalElementRef.nativeElement);
this.terminalHandler.registerTerminal(this.type, this.terminalElementRef().nativeElement);
this.setResizeObserver();
@ -57,7 +57,7 @@ export class Terminal implements AfterViewInit {
this.resize$.next();
});
resizeObserver.observe(this.terminalElementRef.nativeElement);
resizeObserver.observe(this.terminalElementRef().nativeElement);
this.destroyRef.onDestroy(() => resizeObserver.disconnect());
}

View file

@ -137,12 +137,13 @@ describe('Tutorial', () => {
setupResetRevealAnswerValues();
fixture.detectChanges();
if (!component.revealAnswerButton) throw new Error('revealAnswerButton is undefined');
const revealAnswerButton = component.revealAnswerButton();
if (!revealAnswerButton) throw new Error('revealAnswerButton is undefined');
const revealAnswerSpy = spyOn(component['embeddedTutorialManager'], 'revealAnswer');
const resetRevealAnswerSpy = spyOn(component['embeddedTutorialManager'], 'resetRevealAnswer');
component.revealAnswerButton.nativeElement.click();
revealAnswerButton.nativeElement.click();
expect(revealAnswerSpy).not.toHaveBeenCalled();
expect(resetRevealAnswerSpy).toHaveBeenCalled();
@ -152,35 +153,37 @@ describe('Tutorial', () => {
setupRevealAnswerValues();
fixture.detectChanges();
if (!component.revealAnswerButton) throw new Error('revealAnswerButton is undefined');
const revealAnswerButton = component.revealAnswerButton();
if (!revealAnswerButton) throw new Error('revealAnswerButton is undefined');
const embeddedTutorialManagerRevealAnswerSpy = spyOn(
component['embeddedTutorialManager'],
'revealAnswer',
);
component.revealAnswerButton.nativeElement.click();
revealAnswerButton.nativeElement.click();
expect(embeddedTutorialManagerRevealAnswerSpy).toHaveBeenCalled();
await fixture.whenStable();
fixture.detectChanges();
expect(component.revealAnswerButton.nativeElement.textContent?.trim()).toBe('Reset');
expect(revealAnswerButton.nativeElement.textContent?.trim()).toBe('Reset');
});
it('should not reveal the answer when button is disabled', async () => {
setupDisabledRevealAnswerValues();
fixture.detectChanges();
if (!component.revealAnswerButton) throw new Error('revealAnswerButton is undefined');
const revealAnswerButton = component.revealAnswerButton();
if (!revealAnswerButton) throw new Error('revealAnswerButton is undefined');
spyOn(component, 'canRevealAnswer').and.returnValue(false);
const handleRevealAnswerSpy = spyOn(component, 'handleRevealAnswer');
component.revealAnswerButton.nativeElement.click();
revealAnswerButton.nativeElement.click();
expect(component.revealAnswerButton.nativeElement.getAttribute('disabled')).toBeDefined();
expect(revealAnswerButton.nativeElement.getAttribute('disabled')).toBeDefined();
expect(handleRevealAnswerSpy).not.toHaveBeenCalled();
});
@ -188,6 +191,6 @@ describe('Tutorial', () => {
setupNoRevealAnswerValues();
fixture.detectChanges();
expect(component.revealAnswerButton).toBe(undefined);
expect(component.revealAnswerButton()).toBe(undefined);
});
});

View file

@ -21,7 +21,7 @@ import {
Signal,
signal,
Type,
ViewChild,
viewChild,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {
@ -72,11 +72,10 @@ const INTRODUCTION_LABEL = 'Introduction';
providers: [SplitResizerHandler],
})
export default class Tutorial {
@ViewChild('content') content!: ElementRef<HTMLDivElement>;
@ViewChild('editor') editor: ElementRef<HTMLDivElement> | undefined;
@ViewChild('resizer') resizer!: ElementRef<HTMLDivElement>;
@ViewChild('revealAnswerButton')
readonly revealAnswerButton: ElementRef<HTMLButtonElement> | undefined;
readonly content = viewChild<ElementRef<HTMLDivElement>>('content');
readonly editor = viewChild<ElementRef<HTMLDivElement>>('editor');
readonly resizer = viewChild.required<ElementRef<HTMLDivElement>>('resizer');
readonly revealAnswerButton = viewChild<ElementRef<HTMLButtonElement>>('revealAnswerButton');
private readonly changeDetectorRef = inject(ChangeDetectorRef);
private readonly environmentInjector = inject(EnvironmentInjector);
@ -124,7 +123,12 @@ export default class Tutorial {
const destroyRef = inject(DestroyRef);
afterNextRender(() => {
this.splitResizerHandler.init(this.elementRef, this.content, this.resizer, this.editor);
this.splitResizerHandler.init(
this.elementRef,
this.content()!,
this.resizer(),
this.editor(),
);
from(this.loadEmbeddedEditorComponent())
.pipe(takeUntilDestroyed(destroyRef))