mirror of
https://github.com/bitwarden/browser
synced 2026-02-24 16:43:27 +00:00
[PM-32372] Added testid for table and then fixed tech debt (#19066)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Directive, OnDestroy } from "@angular/core";
|
||||
import { Directive, OnDestroy, signal } from "@angular/core";
|
||||
import { FormControl, FormGroup } from "@angular/forms";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { combineLatest, filter, map, Observable, Subject, switchMap, takeUntil } from "rxjs";
|
||||
@@ -22,9 +22,9 @@ import { EventExportService } from "../../tools/event-export";
|
||||
|
||||
@Directive()
|
||||
export abstract class BaseEventsComponent implements OnDestroy {
|
||||
loading = true;
|
||||
loaded = false;
|
||||
events: EventView[];
|
||||
readonly loading = signal(true);
|
||||
readonly loaded = signal(false);
|
||||
readonly events = signal<EventView[]>([]);
|
||||
dirtyDates = true;
|
||||
continuationToken: string;
|
||||
canUseSM = false;
|
||||
@@ -115,7 +115,7 @@ export abstract class BaseEventsComponent implements OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.loading.set(true);
|
||||
|
||||
const dates = this.parseDates();
|
||||
if (dates == null) {
|
||||
@@ -131,7 +131,7 @@ export abstract class BaseEventsComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
promise = null;
|
||||
this.loading = false;
|
||||
this.loading.set(false);
|
||||
};
|
||||
|
||||
loadEvents = async (clearExisting: boolean) => {
|
||||
@@ -140,7 +140,7 @@ export abstract class BaseEventsComponent implements OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.loading.set(true);
|
||||
let events: EventView[] = [];
|
||||
let promise: Promise<any>;
|
||||
promise = this.loadAndParseEvents(
|
||||
@@ -153,14 +153,16 @@ export abstract class BaseEventsComponent implements OnDestroy {
|
||||
this.continuationToken = result.continuationToken;
|
||||
events = result.events;
|
||||
|
||||
if (!clearExisting && this.events != null && this.events.length > 0) {
|
||||
this.events = this.events.concat(events);
|
||||
if (!clearExisting && this.events() != null && this.events().length > 0) {
|
||||
this.events.update((current) => {
|
||||
return [...current, ...events];
|
||||
});
|
||||
} else {
|
||||
this.events = events;
|
||||
this.events.set(events);
|
||||
}
|
||||
|
||||
this.dirtyDates = false;
|
||||
this.loading = false;
|
||||
this.loading.set(false);
|
||||
promise = null;
|
||||
};
|
||||
|
||||
@@ -227,7 +229,7 @@ export abstract class BaseEventsComponent implements OnDestroy {
|
||||
|
||||
private async export(start: string, end: string) {
|
||||
let continuationToken = this.continuationToken;
|
||||
let events = [].concat(this.events);
|
||||
let events = [].concat(this.events());
|
||||
|
||||
while (continuationToken != null) {
|
||||
const result = await this.loadAndParseEvents(start, end, continuationToken);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
@let usePlaceHolderEvents = !organization?.useEvents;
|
||||
|
||||
<app-header>
|
||||
<span
|
||||
bitBadge
|
||||
variant="primary"
|
||||
slot="title-suffix"
|
||||
class="tw-ml-2 tw-mt-1.5 tw-inline-flex tw-items-center"
|
||||
*ngIf="usePlaceHolderEvents"
|
||||
>
|
||||
{{ "upgrade" | i18n }}
|
||||
</span>
|
||||
@if (usePlaceHolderEvents) {
|
||||
<span
|
||||
bitBadge
|
||||
variant="primary"
|
||||
slot="title-suffix"
|
||||
class="tw-ml-2 tw-mt-1.5 tw-inline-flex tw-items-center"
|
||||
>
|
||||
{{ "upgrade" | i18n }}
|
||||
</span>
|
||||
}
|
||||
</app-header>
|
||||
<div class="tw-mb-4" [formGroup]="eventsForm">
|
||||
<div class="tw-mt-4 tw-flex tw-items-center">
|
||||
@@ -61,79 +63,87 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<bit-callout
|
||||
type="info"
|
||||
[title]="'upgradeEventLogTitleMessage' | i18n"
|
||||
*ngIf="loaded && usePlaceHolderEvents"
|
||||
>
|
||||
{{ "upgradeEventLogMessage" | i18n }}
|
||||
</bit-callout>
|
||||
<ng-container *ngIf="!loaded">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loaded">
|
||||
@let displayedEvents = organization?.useEvents ? events : placeholderEvents;
|
||||
@if (loaded() && usePlaceHolderEvents) {
|
||||
<bit-callout type="info" [title]="'upgradeEventLogTitleMessage' | i18n">
|
||||
{{ "upgradeEventLogMessage" | i18n }}
|
||||
</bit-callout>
|
||||
}
|
||||
|
||||
<p *ngIf="!displayedEvents || !displayedEvents.length">{{ "noEventsInList" | i18n }}</p>
|
||||
<bit-table *ngIf="displayedEvents && displayedEvents.length">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "timestamp" | i18n }}</th>
|
||||
<th bitCell>{{ "client" | i18n }}</th>
|
||||
<th bitCell>{{ "member" | i18n }}</th>
|
||||
<th bitCell>{{ "event" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body>
|
||||
<tr bitRow *ngFor="let e of displayedEvents; index as i" alignContent="top">
|
||||
<td bitCell class="tw-whitespace-nowrap">
|
||||
{{ i > 4 && usePlaceHolderEvents ? "******" : (e.date | date: "medium") }}
|
||||
</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.appName }}, {{ e.ip }}">{{ e.appName }}</span>
|
||||
</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.userEmail }}">{{ e.userName }}</span>
|
||||
</td>
|
||||
<td bitCell [innerHTML]="e.message"></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[bitAction]="loadMoreEvents"
|
||||
*ngIf="continuationToken"
|
||||
>
|
||||
{{ "loadMore" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
@if (!loaded()) {
|
||||
<ng-container>
|
||||
<bit-icon
|
||||
class="bwi-lg bwi-spin tw-text-muted"
|
||||
name="bwi-spinner"
|
||||
aria-hidden="true"
|
||||
></bit-icon>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
}
|
||||
@if (loaded()) {
|
||||
<ng-container>
|
||||
@let displayedEvents = organization?.useEvents ? events() : placeholderEvents;
|
||||
|
||||
<ng-container *ngIf="loaded && usePlaceHolderEvents">
|
||||
<div
|
||||
class="tw-relative tw--top-72 tw-bg-background tw-bg-opacity-90 tw-pb-5 tw-flex tw-items-center tw-justify-center tw-h-[19rem]"
|
||||
>
|
||||
<div
|
||||
class="tw-bg-background tw-max-w-xl tw-flex-col tw-justify-center tw-text-center tw-p-5 tw-px-10 tw-rounded tw-border-0 tw-border-b tw-border-secondary-300 tw-border-solid tw-mt-5"
|
||||
>
|
||||
<i class="bwi bwi-2x bwi-business tw-text-primary-600"></i>
|
||||
@if (!displayedEvents || !displayedEvents.length) {
|
||||
<p>{{ "noEventsInList" | i18n }}</p>
|
||||
}
|
||||
|
||||
<p class="tw-font-medium tw-mt-2">
|
||||
{{ "upgradeEventLogTitleMessage" | i18n }}
|
||||
</p>
|
||||
<p>
|
||||
{{ "upgradeForFullEventsMessage" | i18n }}
|
||||
</p>
|
||||
|
||||
<button type="button" class="tw-mt-1" bitButton buttonType="primary" (click)="changePlan()">
|
||||
{{ "changeBillingPlan" | i18n }}
|
||||
@if (displayedEvents && displayedEvents.length) {
|
||||
<bit-table data-testid="events-table">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "timestamp" | i18n }}</th>
|
||||
<th bitCell>{{ "client" | i18n }}</th>
|
||||
<th bitCell>{{ "member" | i18n }}</th>
|
||||
<th bitCell>{{ "event" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body>
|
||||
@for (e of displayedEvents; track i; let i = $index) {
|
||||
<tr bitRow alignContent="top">
|
||||
<td bitCell class="tw-whitespace-nowrap">
|
||||
{{ i > 4 && usePlaceHolderEvents ? "******" : (e.date | date: "medium") }}
|
||||
</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.appName }}, {{ e.ip }}">{{ e.appName }}</span>
|
||||
</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.userEmail }}">{{ e.userName }}</span>
|
||||
</td>
|
||||
<td bitCell [innerHTML]="e.message"></td>
|
||||
</tr>
|
||||
}
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
}
|
||||
@if (continuationToken) {
|
||||
<button type="button" bitButton buttonType="primary" [bitAction]="loadMoreEvents">
|
||||
{{ "loadMore" | i18n }}
|
||||
</button>
|
||||
}
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@if (loaded() && usePlaceHolderEvents) {
|
||||
<ng-container>
|
||||
<div
|
||||
class="tw-relative tw--top-72 tw-bg-background tw-bg-opacity-90 tw-pb-5 tw-flex tw-items-center tw-justify-center tw-h-[19rem]"
|
||||
>
|
||||
<div
|
||||
class="tw-bg-background tw-max-w-xl tw-flex-col tw-justify-center tw-text-center tw-p-5 tw-px-10 tw-rounded tw-border-0 tw-border-b tw-border-secondary-300 tw-border-solid tw-mt-5"
|
||||
>
|
||||
<i class="bwi bwi-2x bwi-business tw-text-primary-600"></i>
|
||||
|
||||
<p class="tw-font-medium tw-mt-2">
|
||||
{{ "upgradeEventLogTitleMessage" | i18n }}
|
||||
</p>
|
||||
<p>
|
||||
{{ "upgradeForFullEventsMessage" | i18n }}
|
||||
</p>
|
||||
|
||||
<button type="button" class="tw-mt-1" bitButton buttonType="primary" (click)="changePlan()">
|
||||
{{ "changeBillingPlan" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Component, OnDestroy, OnInit, ChangeDetectionStrategy } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { concatMap, firstValueFrom, lastValueFrom, map, of, switchMap, takeUntil, tap } from "rxjs";
|
||||
|
||||
@@ -47,9 +47,8 @@ const EVENT_SYSTEM_USER_TO_TRANSLATION: Record<EventSystemUser, string> = {
|
||||
[EventSystemUser.BitwardenPortal]: "system",
|
||||
};
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: "events.component.html",
|
||||
imports: [SharedModule, HeaderModule],
|
||||
})
|
||||
@@ -168,7 +167,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
|
||||
}
|
||||
}
|
||||
await this.refreshEvents();
|
||||
this.loaded = true;
|
||||
this.loaded.set(true);
|
||||
}
|
||||
|
||||
protected requestEvents(startDate: string, endDate: string, continuationToken: string) {
|
||||
|
||||
@@ -50,56 +50,64 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="!loaded">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loaded">
|
||||
<p *ngIf="!events || !events.length">{{ "noEventsInList" | i18n }}</p>
|
||||
<bit-table *ngIf="events && events.length">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "timestamp" | i18n }}</th>
|
||||
<th bitCell>{{ "device" | i18n }}</th>
|
||||
<th bitCell>{{ "user" | i18n }}</th>
|
||||
<th bitCell>{{ "event" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body>
|
||||
<tr bitRow *ngFor="let e of events" alignContent="top">
|
||||
<td bitCell class="tw-whitespace-nowrap">{{ e.date | date: "medium" }}</td>
|
||||
<td bitCell>
|
||||
<i
|
||||
class="tw-text-muted bwi bwi-lg {{ e.appIcon }}"
|
||||
title="{{ e.appName }}, {{ e.ip }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ e.appName }}, {{ e.ip }}</span>
|
||||
</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.userEmail }}">{{ e.userName }}</span>
|
||||
</td>
|
||||
<td bitCell [innerHTML]="e.message"></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<button
|
||||
bitButton
|
||||
type="button"
|
||||
buttonType="primary"
|
||||
[bitAction]="loadMoreEvents"
|
||||
*ngIf="continuationToken"
|
||||
>
|
||||
<i
|
||||
*ngIf="loading"
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
@if (!loaded()) {
|
||||
<ng-container>
|
||||
<bit-icon
|
||||
class="bwi-lg bwi-spin tw-text-muted"
|
||||
name="bwi-spinner"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{{ "loadMore" | i18n }}</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
></bit-icon>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
}
|
||||
@if (loaded()) {
|
||||
<ng-container>
|
||||
@if (!events() || !events().length) {
|
||||
<p>{{ "noEventsInList" | i18n }}</p>
|
||||
}
|
||||
|
||||
@if (events() && events().length) {
|
||||
<bit-table data-testid="events-table">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "timestamp" | i18n }}</th>
|
||||
<th bitCell>{{ "device" | i18n }}</th>
|
||||
<th bitCell>{{ "user" | i18n }}</th>
|
||||
<th bitCell>{{ "event" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body>
|
||||
@for (e of events(); track i; let i = $index) {
|
||||
<tr bitRow alignContent="top">
|
||||
<td bitCell class="tw-whitespace-nowrap">{{ e.date | date: "medium" }}</td>
|
||||
<td bitCell>
|
||||
<i
|
||||
class="tw-text-muted bwi bwi-lg {{ e.appIcon }}"
|
||||
title="{{ e.appName }}, {{ e.ip }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ e.appName }}, {{ e.ip }}</span>
|
||||
</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.userEmail }}">{{ e.userName }}</span>
|
||||
</td>
|
||||
<td bitCell [innerHTML]="e.message"></td>
|
||||
</tr>
|
||||
}
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
}
|
||||
@if (continuationToken) {
|
||||
<button bitButton type="button" buttonType="primary" [bitAction]="loadMoreEvents">
|
||||
@if (loading()) {
|
||||
<bit-icon
|
||||
class="bwi-lg bwi-spin tw-text-muted"
|
||||
name="bwi-spinner"
|
||||
aria-hidden="true"
|
||||
></bit-icon>
|
||||
}
|
||||
<span>{{ "loadMore" | i18n }}</span>
|
||||
</button>
|
||||
}
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit {
|
||||
this.providerUsersUserIdMap.set(u.userId, { name: name, email: u.email });
|
||||
});
|
||||
await this.refreshEvents();
|
||||
this.loaded = true;
|
||||
this.loaded.set(true);
|
||||
}
|
||||
|
||||
protected requestEvents(startDate: string, endDate: string, continuationToken: string) {
|
||||
|
||||
@@ -47,41 +47,47 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="!loaded">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loaded">
|
||||
<p *ngIf="!events || !events.length">{{ "noEventsInList" | i18n }}</p>
|
||||
<bit-table *ngIf="events && events.length">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "timestamp" | i18n }}</th>
|
||||
<th bitCell>{{ "client" | i18n }}</th>
|
||||
<th bitCell>{{ "event" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body>
|
||||
<tr bitRow *ngFor="let e of events" alignContent="top">
|
||||
<td bitCell class="tw-whitespace-nowrap">{{ e.date | date: "medium" }}</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.appName }}, {{ e.ip }}">{{ e.appName }}</span>
|
||||
</td>
|
||||
<td bitCell [innerHTML]="e.message"></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[bitAction]="loadMoreEvents"
|
||||
*ngIf="continuationToken"
|
||||
>
|
||||
<span>{{ "loadMore" | i18n }}</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
@if (!loaded()) {
|
||||
<ng-container>
|
||||
<bit-icon
|
||||
class="bwi-lg bwi-spin tw-text-muted"
|
||||
name="bwi-spinner"
|
||||
aria-hidden="true"
|
||||
></bit-icon>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
}
|
||||
@if (loaded()) {
|
||||
<ng-container>
|
||||
@if (!events() || !events().length) {
|
||||
<p>{{ "noEventsInList" | i18n }}</p>
|
||||
}
|
||||
@if (events() && events().length) {
|
||||
<bit-table data-testid="events-table">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "timestamp" | i18n }}</th>
|
||||
<th bitCell>{{ "client" | i18n }}</th>
|
||||
<th bitCell>{{ "event" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body>
|
||||
@for (e of events(); track i; let i = $index) {
|
||||
<tr bitRow alignContent="top">
|
||||
<td bitCell class="tw-whitespace-nowrap">{{ e.date | date: "medium" }}</td>
|
||||
<td bitCell>
|
||||
<span title="{{ e.appName }}, {{ e.ip }}">{{ e.appName }}</span>
|
||||
</td>
|
||||
<td bitCell [innerHTML]="e.message"></td>
|
||||
</tr>
|
||||
}
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
}
|
||||
@if (continuationToken) {
|
||||
<button type="button" bitButton buttonType="primary" [bitAction]="loadMoreEvents">
|
||||
<span>{{ "loadMore" | i18n }}</span>
|
||||
</button>
|
||||
}
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { takeUntil } from "rxjs";
|
||||
|
||||
@@ -17,9 +17,8 @@ import { EventExportService } from "@bitwarden/web-vault/app/tools/event-export"
|
||||
|
||||
import { ServiceAccountEventLogApiService } from "./service-account-event-log-api.service";
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: "sm-service-accounts-events",
|
||||
templateUrl: "./service-accounts-events.component.html",
|
||||
standalone: false,
|
||||
@@ -69,7 +68,7 @@ export class ServiceAccountEventsComponent
|
||||
|
||||
async load() {
|
||||
await this.refreshEvents();
|
||||
this.loaded = true;
|
||||
this.loaded.set(true);
|
||||
}
|
||||
|
||||
protected requestEvents(startDate: string, endDate: string, continuationToken: string) {
|
||||
|
||||
Reference in New Issue
Block a user