Skip to content

Commit

Permalink
test: Add stories for ProjectMetadata component
Browse files Browse the repository at this point in the history
  • Loading branch information
MoritzWeber0 committed Apr 10, 2024
1 parent 4bb1873 commit f7f4ce8
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 54 deletions.
19 changes: 18 additions & 1 deletion frontend/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import docJson from '../documentation.json';
import { HttpClientModule } from '@angular/common/http';
import { importProvidersFrom } from '@angular/core';
import { applicationConfig } from '@storybook/angular';
import { ToastrModule } from 'ngx-toastr';
import { RouterModule } from '@angular/router';

setCompodocJson(docJson);

Expand All @@ -23,7 +25,22 @@ const preview: Preview = {
},
decorators: [
applicationConfig({
providers: [importProvidersFrom(HttpClientModule)],
providers: [
importProvidersFrom(HttpClientModule),
importProvidersFrom(
ToastrModule.forRoot({
positionClass: 'toast-bottom-left',
timeOut: 10000,
extendedTimeOut: 2000,
maxOpened: 5,
preventDuplicates: true,
countDuplicates: true,
resetTimeoutOnDuplicate: true,
includeTitleDuplicates: true,
}),
),
importProvidersFrom(RouterModule.forRoot([])),
],
}),
],
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import { T4CSettingsWrapperComponent } from './settings/modelsources/t4c-setting
import { T4CSettingsComponent } from './settings/modelsources/t4c-settings/t4c-settings.component';
import { SettingsComponent } from './settings/settings.component';

const routes: Routes = [
export const routes: Routes = [
{
path: '',
canActivate: [authGuard],
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ import { UsersProfileComponent } from './users/users-profile/users-profile.compo
PipelineWrapperComponent,
ProjectAuditLogComponent,
ProjectDetailsComponent,
ProjectMetadataComponent,
ProjectOverviewComponent,
ProjectUserSettingsComponent,
ProjectWrapperComponent,
Expand Down Expand Up @@ -298,6 +297,7 @@ import { UsersProfileComponent } from './users/users-profile/users-profile.compo
NgxSkeletonLoaderModule.forRoot(),
ModelComplexityBadgeComponent,
OverlayModule,
ProjectMetadataComponent,
ReactiveFormsModule,
ToastrModule.forRoot({
positionClass: 'toast-bottom-left',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const modelBadge =
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MzYuMCIgaGVpZ2h0PSIxMjAuMCIgdmlld0JveD0iMCAwIDEzNCAzMCI+PGcgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMy4yIj4KPHJlY3QgZmlsbD0iI0ZGRiIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjAuMiIgeD0iMSIgeT0iMCIgd2lkdGg9IjEzMiIgaGVpZ2h0PSIzMCIgLz4KPHJlY3QgZmlsbD0iI0ZGRiIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjAuMiIgeD0iMjMuOCIgeT0iMS40IiB3aWR0aD0iMTA4IiBoZWlnaHQ9IjIyIiAvPgo8ZyBzdHJva2U9IiM1NTUiIHN0cm9rZS13aWR0aD0iLjMiPjxwYXRoIGZpbGw9IiNmZmRkODciIGQ9Ik0zIDUuNSBoMS45IFY3LjQgaC0yIHoiLz48cGF0aCBmaWxsPSIjYTVjMmU2IiBkPSJNMy44IDYuNCBoMiB2MS44IGgtMiB6Ii8+PC9nPjx0ZXh0IHg9IjciIHk9IjYuNCI+MTI3MzwvdGV4dD4KPHRleHQgeD0iNyIgeT0iOS40Ij5vYmplY3RzPC90ZXh0Pgo8ZyBmb250LXNpemU9IjMuMiI+CjxyZWN0IGZpbGw9IiNmZmRkODciIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIwIiB4PSIyNC4yIiB5PSIzLjQiIHdpZHRoPSIxMi45ODk3ODc5MDI1OTIzMDEiIGhlaWdodD0iOCIgLz4KPHRleHQgeD0iMjUuMiIgeT0iOC40Ij4xMiU8L3RleHQ+CjxyZWN0IGZpbGw9IiM5MWNjODQiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIwIiB4PSIzNy4xODk3ODc5MDI1OTIzIiB5PSIzLjQiIHdpZHRoPSIzNi41MTg0NjAzMjk5MjkzMDQiIGhlaWdodD0iOCIgLz4KPHRleHQgeD0iMzguMTg5Nzg3OTAyNTkyMyIgeT0iOC40Ij4zNSU8L3RleHQ+CjxyZWN0IGZpbGw9IiNhNWMyZTYiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIwIiB4PSI3My43MDgyNDgyMzI1MjE2MSIgeT0iMy40IiB3aWR0aD0iNDEuMTc1MTc2NzQ3ODM5NzUiIGhlaWdodD0iOCIgLz4KPHRleHQgeD0iNzQuNzA4MjQ4MjMyNTIxNjEiIHk9IjguNCI+NDAlPC90ZXh0Pgo8cmVjdCBmaWxsPSIjZjg5ZjlmIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMCIgeD0iMTE0Ljg4MzQyNDk4MDM2MTM3IiB5PSIzLjQiIHdpZHRoPSIxMy4zMTY1NzUwMTk2Mzg2NDgiIGhlaWdodD0iOCIgLz4KPHRleHQgeD0iMTE1Ljg4MzQyNDk4MDM2MTM3IiB5PSI4LjQiPjEzJTwvdGV4dD4KPC9nPgo8ZyBzdHJva2U9IiM1NTUiIHN0cm9rZS13aWR0aD0iLjIiPjxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0yLjUgMTUuMjAwMDAwMDAwMDAwMDAxIGgzLjYgdjMuMSBIMi41IHoiLz48cGF0aCBmaWxsPSIjYTVjMmU2IiBkPSJNMyAxNS44IGgxdi43IEgzeiBtLjggMS4yaDF2LjdoLTF6IG0uOS0xLjFoLjh2LjZoLS44eiIvPjxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0zLjkgMTYuMiBoLjggbS0xLjMuMy40LjggbTEgLjEuNC0uOSIvPjwvZz48dGV4dCB4PSI3IiB5PSIxNi40Ij41MjwvdGV4dD4KPHRleHQgeD0iNyIgeT0iMTkuNCI+ZGlhZ3JhbXM8L3RleHQ+CjxnIGZvbnQtc2l6ZT0iMy4yIj4KPHJlY3QgZmlsbD0iI2ZmZGQ4NyIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjAiIHg9IjI0LjIiIHk9IjEzLjQiIHdpZHRoPSIwLjAiIGhlaWdodD0iOCIgLz4KPHJlY3QgZmlsbD0iIzkxY2M4NCIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjAiIHg9IjI0LjIiIHk9IjEzLjQiIHdpZHRoPSI2NC4wIiBoZWlnaHQ9IjgiIC8+Cjx0ZXh0IHg9IjI1LjIiIHk9IjE4LjQiPjYyJTwvdGV4dD4KPHJlY3QgZmlsbD0iI2E1YzJlNiIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjAiIHg9Ijg4LjIiIHk9IjEzLjQiIHdpZHRoPSIzNC4wIiBoZWlnaHQ9IjgiIC8+Cjx0ZXh0IHg9Ijg5LjIiIHk9IjE4LjQiPjMzJTwvdGV4dD4KPHJlY3QgZmlsbD0iI2Y4OWY5ZiIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjAiIHg9IjEyMi4yIiB5PSIxMy40IiB3aWR0aD0iNi4wIiBoZWlnaHQ9IjgiIC8+CjwvZz4KPGcgZm9udC1zaXplPSIyLjgiIGZpbGw9IiM1NTUiPgo8cmVjdCBmaWxsPSIjZmZkZDg3IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMCIgeD0iMyIgeT0iMjUiIHdpZHRoPSI1IiBoZWlnaHQ9IjMiIC8+Cjx0ZXh0IHg9IjkiIHk9IjI3LjYiPk9wZXJhdGlvbmFsIEFuYWx5c2lzPC90ZXh0Pgo8cmVjdCBmaWxsPSIjOTFjYzg0IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMCIgeD0iMzciIHk9IjI1IiB3aWR0aD0iNSIgaGVpZ2h0PSIzIiAvPgo8dGV4dCB4PSI0MyIgeT0iMjcuNiI+U3lzdGVtIEFuYWx5c2lzPC90ZXh0Pgo8cmVjdCBmaWxsPSIjYTVjMmU2IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMCIgeD0iNjciIHk9IjI1IiB3aWR0aD0iNSIgaGVpZ2h0PSIzIiAvPgo8dGV4dCB4PSI3MyIgeT0iMjcuNiI+TG9naWNhbCBBcmNoaXRlY3R1cmU8L3RleHQ+CjxyZWN0IGZpbGw9IiNmODlmOWYiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIwIiB4PSIxMDAiIHk9IjI1IiB3aWR0aD0iNSIgaGVpZ2h0PSIzIiAvPgo8dGV4dCB4PSIxMDYiIHk9IjI3LjYiPlBoeXNpY2FsIEFyY2hpdGVjdHVyZTwvdGV4dD4KPC9nPgo8L2c+Cjwvc3ZnPgo=';

const meta: Meta<ModelComplexityBadgeComponent> = {
title: 'Model Components/ModelBadge',
title: 'Model Components / Model Complexity Badge',
component: ModelComplexityBadgeComponent,
parameters: {
chromatic: { viewports: [500] },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ <h2 id="title" class="text-xl font-medium">Project Information</h2>
class="collab-card [p-12px] m-[10px] !flex grow flex-col justify-between"
>
<div class="flex flex-col">
<div class="break-all" *ngIf="projectService.project$ | async">
<h2 id="title" class="text-xl font-medium">
{{ (projectService.project$ | async)!.name }}
</h2>
</div>
<div *ngIf="(projectService.project$ | async) === undefined">
@if (project !== undefined) {
<div class="break-all">
<h2 id="title" class="text-xl font-medium">
{{ project!.name }}
</h2>
</div>
} @else {
<ngx-skeleton-loader
[theme]="{
'border-radius': '5px',
Expand All @@ -23,18 +24,12 @@ <h2 id="title" class="text-xl font-medium">
border: '1px solid white'
}"
></ngx-skeleton-loader>
</div>
<div
*ngIf="(projectService.project$ | async) !== undefined"
class="grow text-sm"
id="description"
>
{{
(projectService.project$ | async)!.description ||
"No description available."
}}
</div>
<div *ngIf="(projectService.project$ | async) === undefined">
}
@if (project !== undefined) {
<div class="grow text-sm" id="description">
{{ project!.description || "No description available." }}
</div>
} @else {
<ngx-skeleton-loader
[theme]="{
'border-radius': '5px',
Expand All @@ -43,39 +38,38 @@ <h2 id="title" class="text-xl font-medium">
border: '1px solid white'
}"
></ngx-skeleton-loader>
</div>
}
</div>
<div
class="flex flex-wrap items-center gap-2"
*ngIf="projectUserService.verifyRole('manager')"
>
<span
matTooltip="The project can't be deleted since it has linked models"
[matTooltipDisabled]="canDelete"
>
<button
[disabled]="!canDelete"
(click)="deleteProject()"
mat-flat-button
color="warn"
@if (projectUserService.verifyRole("manager")) {
<div class="flex flex-wrap items-center gap-2">
<span
matTooltip="The project can't be deleted since it has linked models"
[matTooltipDisabled]="canDelete"
>
<mat-icon class="mat-icon-position left">delete_forever</mat-icon
>Delete project
</button>
</span>
<a mat-raised-button (click)="toggleArchive()">
<mat-icon class="mat-icon-position left">archive</mat-icon>
{{ project?.is_archived ? "Unarchive" : "Archive" }}
</a>
<a
[disabled]="(projectService.project$ | async) === undefined"
mat-raised-button
routerLink="metadata"
id="modify"
>
<mat-icon class="mat-icon-position left">settings</mat-icon> Modify
project
</a>
</div>
<button
[disabled]="!canDelete"
(click)="deleteProject()"
mat-flat-button
color="warn"
>
<mat-icon class="mat-icon-position left">delete_forever</mat-icon
>Delete project
</button>
</span>
<a mat-raised-button (click)="toggleArchive()">
<mat-icon class="mat-icon-position left">archive</mat-icon>
{{ project?.is_archived ? "Unarchive" : "Archive" }}
</a>
<a
[disabled]="project === undefined"
mat-raised-button
routerLink="metadata"
id="modify"
>
<mat-icon class="mat-icon-position left">settings</mat-icon> Modify
project
</a>
</div>
}
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
*/

import { Component, OnInit } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ActivatedRoute, Router } from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { filter } from 'rxjs';
import { ConfirmationDialogComponent } from 'src/app/helpers/confirmation-dialog/confirmation-dialog.component';
import { ToastService } from 'src/app/helpers/toast/toast.service';
Expand All @@ -21,6 +25,13 @@ import {
@UntilDestroy()
@Component({
selector: 'app-project-metadata',
standalone: true,
imports: [
MatIconModule,
NgxSkeletonLoaderModule,
MatButtonModule,
MatTooltipModule,
],
templateUrl: './project-metadata.component.html',
styleUrls: ['./project-metadata.component.css'],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{/*
SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
SPDX-License-Identifier: Apache-2.0
*/}

import * as ProjectMetadata from "./project-metadata.stories.ts";
import {Meta, Title, Story, Canvas} from "@storybook/blocks";

<Meta of={ProjectMetadata} />

<Title />

The Project metadata let's users know about the project name, description and status.

When it's loaded, the metadata view looks like:
<Story of={ProjectMetadata.Loading}/>

For example, this is the view for a normal user in the project:
<Story of={ProjectMetadata.NormalUser}/>

Project leads have the option to modify some settings:
<Story of={ProjectMetadata.ProjectAdmin} />

This is how an archived projects looks like:
<Story of={ProjectMetadata.Archived} />

If there are linked models in the project, the delete button is disabled:
<Story of={ProjectMetadata.CantDelete} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { Meta, moduleMetadata, StoryObj } from '@storybook/angular';

import {
ProjectUserRole,
ProjectUserService,
} from 'src/app/projects/project-detail/project-users/service/project-user.service';
import { Project } from 'src/app/projects/service/project.service';
import { ProjectMetadataComponent } from './project-metadata.component';

class MockProjectUserService implements Partial<ProjectUserService> {
user: ProjectUserRole;

constructor(user: ProjectUserRole) {
this.user = user;
}

verifyRole(requiredRole: ProjectUserRole): boolean {
const roles = ['user', 'manager', 'administrator'];
return roles.indexOf(requiredRole) <= roles.indexOf(this.user);
}
}

const meta: Meta<ProjectMetadataComponent> = {
title: 'Project Components / Project Metadata',
component: ProjectMetadataComponent,
};

export default meta;
type Story = StoryObj<ProjectMetadataComponent>;

const project: Project = {
name: 'test',
description: 'test',
type: 'general',
visibility: 'internal',
is_archived: false,
slug: 'test',
users: {
leads: 1,
contributors: 1,
subscribers: 1,
},
};

export const Loading: Story = {
args: {
project: undefined,
},
decorators: [
moduleMetadata({
providers: [
{
provide: ProjectUserService,
useFactory: () => new MockProjectUserService('user'),
},
],
}),
],
};

export const NormalUser: Story = {
args: {
project: project,
},
decorators: [
moduleMetadata({
providers: [
{
provide: ProjectUserService,
useFactory: () => new MockProjectUserService('user'),
},
],
}),
],
};

export const ProjectAdmin: Story = {
args: {
project: project,
canDelete: true,
},
decorators: [
moduleMetadata({
providers: [
{
provide: ProjectUserService,
useFactory: () => new MockProjectUserService('manager'),
},
],
}),
],
};

export const Archived: Story = {
args: {
project: { ...project, is_archived: true },
canDelete: true,
},
decorators: [
moduleMetadata({
providers: [
{
provide: ProjectUserService,
useFactory: () => new MockProjectUserService('manager'),
},
],
}),
],
};

export const CantDelete: Story = {
args: {
project: project,
canDelete: false,
},
decorators: [
moduleMetadata({
providers: [
{
provide: ProjectUserService,
useFactory: () => new MockProjectUserService('manager'),
},
],
}),
],
};

0 comments on commit f7f4ce8

Please sign in to comment.