mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
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:
parent
b972249145
commit
f15f41111b
10 changed files with 76 additions and 79 deletions
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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('/');
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ describe('CodeEditor', () => {
|
|||
component.ngAfterViewInit();
|
||||
|
||||
expect(codeMirrorEditorInitSpy).toHaveBeenCalledWith(
|
||||
component['codeEditorWrapperRef'].nativeElement,
|
||||
component.codeEditorWrapperRef().nativeElement,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
Loading…
Reference in a new issue