Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -111,7 +111,7 @@ export class ExampleViewer {
`example-${this.exampleMetadata()?.id.toString()!}`,
);

this.matTabGroup?.realignInkBar();
this.matTabGroup()?.realignInkBar();

this.listenToMatTabIndexChange();

Expand Down Expand Up @@ -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]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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('/');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
inject,
OnInit,
PLATFORM_ID,
Signal,
viewChild,
} from '@angular/core';
import {isPlatformBrowser} from '@angular/common';
Expand Down Expand Up @@ -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));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe('CodeEditor', () => {
component.ngAfterViewInit();

expect(codeMirrorEditorInitSpy).toHaveBeenCalledWith(
component['codeEditorWrapperRef'].nativeElement,
component.codeEditorWrapperRef().nativeElement,
);
});

Expand Down
64 changes: 32 additions & 32 deletions adev/src/app/editor/code-editor/code-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;

private createFileInputRef?: ElementRef<HTMLInputElement>;
@ViewChild('createFileInput') protected set setFileInputRef(
element: ElementRef<HTMLInputElement>,
) {
if (element) {
element.nativeElement.focus();
this.createFileInputRef = element;
}
}
readonly codeEditorWrapperRef =
viewChild.required<ElementRef<HTMLDivElement>>('codeEditorWrapper');
readonly matTabGroup = viewChild.required(MatTabGroup);

private renameFileInputRef?: ElementRef<HTMLInputElement>;
@ViewChild('renameFileInput') protected set setRenameFileInputRef(
element: ElementRef<HTMLInputElement>,
) {
if (element) {
element.nativeElement.focus();
this.renameFileInputRef = element;
}
}
readonly createFileInputRef = viewChild<ElementRef<HTMLInputElement>>('createFileInput');

readonly renameFileInputRef = viewChild<ElementRef<HTMLInputElement>>('renameFileInput');

private readonly destroyRef = inject(DestroyRef);

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -164,24 +162,25 @@ 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() {
this.isRenamingFile.set(true);
}

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('..')) {
Expand All @@ -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('..')) {
Expand Down Expand Up @@ -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];

Expand Down
15 changes: 9 additions & 6 deletions adev/src/app/editor/embedded-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
});
}

Expand All @@ -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;

Expand Down
12 changes: 0 additions & 12 deletions adev/src/app/editor/terminal/terminal.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
});
});
8 changes: 4 additions & 4 deletions adev/src/app/editor/terminal/terminal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import {
DestroyRef,
ElementRef,
Input,
ViewChild,
ViewEncapsulation,
inject,
viewChild,
} from '@angular/core';

import {debounceTime} from 'rxjs/operators';
Expand All @@ -35,15 +35,15 @@ 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);

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();

Expand All @@ -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());
}
Expand Down
21 changes: 12 additions & 9 deletions adev/src/app/features/tutorial/tutorial.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,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();
Expand All @@ -150,42 +151,44 @@ 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();
});

it('should not render the reveal answer button when there are no answers', () => {
setupNoRevealAnswerValues();
fixture.detectChanges();

expect(component.revealAnswerButton).toBe(undefined);
expect(component.revealAnswerButton()).toBe(undefined);
});
});
Loading