Skip to content

Commit 4bcf023

Browse files
authored
Merge branch 'dev' into 125-visualize-sample-count-for-diseases-and-subptypes-in-browse-form
2 parents 64e9424 + f799bad commit 4bcf023

File tree

62 files changed

+391
-279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+391
-279
lines changed

.cursor/rules/angular.mdc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: true
5+
---
6+
When writing angular code, always use signals and the new @if, @else, @for and @empty control flow.
7+
Component inputs and outputs should be implemented using the signal-based API.
8+
Angular Material is installed and should be used for the UI.

src/app/routes/browse/active-entities/active-entities.component.html renamed to src/app/components/browse-views/active-entities/active-entities.component.html

File renamed without changes.

src/app/routes/browse/active-entities/active-entities.component.scss renamed to src/app/components/browse-views/active-entities/active-entities.component.scss

File renamed without changes.

src/app/routes/browse/active-entities/active-entities.component.spec.ts renamed to src/app/components/browse-views/active-entities/active-entities.component.spec.ts

File renamed without changes.

src/app/routes/browse/active-entities/active-entities.component.ts renamed to src/app/components/browse-views/active-entities/active-entities.component.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { Component, computed, effect, inject, linkedSignal, model } from '@angular/core';
1+
import {
2+
Component,
3+
computed,
4+
effect,
5+
inject,
6+
linkedSignal,
7+
model,
8+
input,
9+
} from '@angular/core';
210
import { MatTabsModule } from '@angular/material/tabs';
311
import { BrowseService } from '../../../services/browse.service';
412
import {
@@ -10,9 +18,9 @@ import {
1018
import { MatCardModule } from '@angular/material/card';
1119
import { MatDialog } from '@angular/material/dialog';
1220
import { MatAnchor, MatButton } from '@angular/material/button';
13-
import { InteractionModalComponent } from '../../../components/interaction-modal/interaction-modal.component';
21+
import { InteractionModalComponent } from '../../interaction-modal/interaction-modal.component';
1422
import { MatTooltip } from '@angular/material/tooltip';
15-
import { ModalsService } from '../../../components/modals-service/modals.service';
23+
import { ModalsService } from '../../modals-service/modals.service';
1624

1725
@Component({
1826
selector: 'app-active-entities',
@@ -25,29 +33,31 @@ export class ActiveEntitiesComponent {
2533
protected BrowseService = BrowseService;
2634
protected activeTabIndex = model<number>(0);
2735
protected modalsService = inject(ModalsService);
28-
protected readonly browseService = inject(BrowseService);
36+
browseService = input.required<BrowseService>();
2937

3038
constructor() {
3139
effect(() => {
32-
this.activeTabIndex.set(this.browseService.lastClicked() === 'node' ? 0 : 1);
40+
this.activeTabIndex.set(
41+
this.browseService().lastClicked() === 'node' ? 0 : 1
42+
);
3343
});
3444
}
3545

36-
nodes$ = this.browseService.activeNodes$;
46+
nodes$ = computed(() => this.browseService().activeNodes$());
3747
gProfilerUrl = computed(() =>
38-
BrowseService.getGProfilerUrlForNodes(this.nodes$()),
48+
BrowseService.getGProfilerUrlForNodes(this.nodes$())
3949
);
40-
edges$ = this.browseService.activeInteractions$;
41-
level$ = this.browseService.level$;
50+
edges$ = computed(() => this.browseService().activeInteractions$());
51+
level$ = computed(() => this.browseService().level$());
4252

4353
openInteractionModal(
44-
interaction: GeneInteraction | TranscriptInteraction,
54+
interaction: GeneInteraction | TranscriptInteraction
4555
): void {
4656
this.dialog.open(InteractionModalComponent, {
4757
data: {
4858
interaction: interaction,
49-
disease: this.browseService.disease$()
50-
}
59+
disease: this.browseService().disease$(),
60+
},
5161
});
5262
}
5363

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
@if (!isLoading$()) {
2+
@if (hasData$()) {
3+
<mat-tab-group [preserveContent]="false" (selectedTabChange)="refresh()">
4+
<mat-tab label="Network">
5+
<div style="display: flex; flex-direction: row">
6+
<app-network [browseService]="browseService()" [refreshSignal]="refresh$()"
7+
style="min-width: 500px; flex-grow: 3"></app-network>
8+
<app-active-entities [browseService]="browseService()"
9+
style="min-width: 200px; width: 25%; flex-grow: 1"></app-active-entities>
10+
</div>
11+
</mat-tab>
12+
@if (level() == 'gene') {
13+
<mat-tab label="Survival analysis">
14+
<app-survival-analysis [browseService]="browseService()" [refresh]="refresh$()"></app-survival-analysis>
15+
</mat-tab>
16+
}
17+
<mat-tab label="Expression heatmap">
18+
<app-heatmap [browseService]="browseService()" [refreshSignal]="refresh$()"></app-heatmap>
19+
</mat-tab>
20+
<mat-tab [label]="(capitalize(level() || '') || 'Gene') + 's'">
21+
<app-nodes [browseService]="browseService()"></app-nodes>
22+
</mat-tab>
23+
<mat-tab label="Interactions">
24+
<app-interactions [browseService]="browseService()"></app-interactions>
25+
</mat-tab>
26+
@if (version$() > 1) {
27+
<mat-tab label="Gene set enrichment">
28+
<app-gsea [browseService]="browseService()" [refresh$]="refresh$"></app-gsea>
29+
</mat-tab>
30+
}
31+
<mat-tab label="Disease similarities" [disabled]="!hasNetworkResults$()">
32+
<app-disease-similarity [browseService]="browseService()" [refreshSignal]="refresh$()"></app-disease-similarity>
33+
</mat-tab>
34+
</mat-tab-group>
35+
} @else {
36+
<div class="card-container" style="margin-top: 50px">
37+
<mat-icon>
38+
warning
39+
</mat-icon>
40+
<p>
41+
The configuration you have selected does not have any data.
42+
</p>
43+
<p>
44+
Consider increasing the thresholds or selecting a different configuration.
45+
</p>
46+
</div>
47+
}
48+
} @else {
49+
<div class="spinner-container">
50+
<mat-spinner mode="indeterminate"></mat-spinner>
51+
</div>
52+
}

src/app/routes/browse/disease-distances/disease-similarity.component.scss renamed to src/app/components/browse-views/browse-views.component.scss

File renamed without changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { BrowseViewsComponent } from './browse-views.component';
4+
5+
describe('BrowseViewsComponent', () => {
6+
let component: BrowseViewsComponent;
7+
let fixture: ComponentFixture<BrowseViewsComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [BrowseViewsComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(BrowseViewsComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Component, computed, inject, input, signal } from '@angular/core';
2+
import { MatSidenavModule } from '@angular/material/sidenav';
3+
import { MatTabsModule } from '@angular/material/tabs';
4+
import { ReactiveFormsModule } from '@angular/forms';
5+
import { MatExpansionModule } from '@angular/material/expansion';
6+
import { InteractionsComponent } from '../../components/browse-views/interactions/interactions.component';
7+
import { NetworkComponent } from '../../components/browse-views/network/network.component';
8+
import { HeatmapComponent } from '../../components/browse-views/heatmap/heatmap.component';
9+
import { BrowseService } from '../../services/browse.service';
10+
import { SurvivalAnalysisComponent } from '../../components/browse-views/survival-analysis/survival-analysis.component';
11+
import { ActiveEntitiesComponent } from '../../components/browse-views/active-entities/active-entities.component';
12+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
13+
import { MatIcon } from '@angular/material/icon';
14+
import { VersionsService } from '../../services/versions.service';
15+
import { NodesComponent } from '../../components/browse-views/nodes/nodes.component';
16+
import { GSEAComponent } from '../../components/browse-views/gsea/gsea.component';
17+
import { DiseaseSimilarityComponent } from '../../components/browse-views/disease-distances/disease-similarity.component';
18+
import { fromEvent } from 'rxjs';
19+
import { capitalize } from 'lodash';
20+
21+
@Component({
22+
selector: 'app-browse-views',
23+
imports: [
24+
MatSidenavModule,
25+
MatTabsModule,
26+
ReactiveFormsModule,
27+
MatExpansionModule,
28+
InteractionsComponent,
29+
NetworkComponent,
30+
HeatmapComponent,
31+
SurvivalAnalysisComponent,
32+
ActiveEntitiesComponent,
33+
MatProgressSpinnerModule,
34+
MatIcon,
35+
NodesComponent,
36+
GSEAComponent,
37+
DiseaseSimilarityComponent,
38+
],
39+
templateUrl: './browse-views.component.html',
40+
styleUrl: './browse-views.component.scss',
41+
})
42+
export class BrowseViewsComponent {
43+
refresh$ = signal(0);
44+
versionService = inject(VersionsService);
45+
browseService = input.required<BrowseService>();
46+
level = computed(() => this.browseService().level$());
47+
version$ = this.versionService.versionReadOnly();
48+
hasData$ = computed(() => this.browseService().nodes$().length > 0);
49+
isLoading$ = computed(() => this.browseService().isLoading$());
50+
rawDataURL$ = computed(() => this.browseService().rawDataURL()());
51+
hasNetworkResults$ = computed(
52+
() => this.browseService().networkResults$() !== undefined
53+
);
54+
hasGseaContrasts$ = computed(
55+
() => this.browseService().possibleComparisons$().length > 0
56+
);
57+
protected readonly capitalize = capitalize;
58+
59+
constructor() {
60+
fromEvent(window, 'resize').subscribe(() => this.refresh());
61+
}
62+
63+
refresh = () => this.refresh$.update((v) => v + 1);
64+
}

src/app/routes/browse/disease-distances/disease-similarity.component.html renamed to src/app/components/browse-views/disease-distances/disease-similarity.component.html

File renamed without changes.

0 commit comments

Comments
 (0)