From 4b32d1f9ddf9958070ad5f6bc288991ef5106ecd Mon Sep 17 00:00:00 2001 From: Vicki League Date: Fri, 16 May 2025 16:02:03 -0400 Subject: [PATCH] Revert "[CL-622][CL-562][CL-621][CL-632] various drawer improvements (#14120)" (#14827) This reverts commit a0429d7d094f3d7267d43596a84afa6f88b73784. --- .../manage/groups.component.html | 2 +- .../members/members.component.html | 2 +- .../organizations/members/members.module.ts | 2 - .../organizations/organization.module.ts | 3 - .../collection-dialog.component.ts | 2 +- .../device-management.component.spec.ts | 14 +- .../add-sponsorship-dialog.component.ts | 4 +- .../free-bitwarden-families.component.ts | 3 +- .../vault-items/vault-items.component.html | 2 +- .../vault-items/vault-items.module.ts | 3 +- .../vault-items/vault-items.stories.ts | 12 +- .../providers/manage/members.component.html | 2 +- .../providers/providers.module.ts | 3 +- .../clients/create-client-dialog.component.ts | 4 +- .../src/dialog/dialog.service.stories.ts | 57 +---- libs/components/src/dialog/dialog.service.ts | 242 +++++------------- .../src/dialog/dialog/dialog.component.html | 40 +-- .../src/dialog/dialog/dialog.component.ts | 37 +-- libs/components/src/dialog/dialogs.mdx | 3 - libs/components/src/dialog/index.ts | 2 +- .../src/drawer/drawer-body.component.ts | 18 +- .../components/src/drawer/drawer.component.ts | 4 +- libs/components/src/drawer/drawer.mdx | 2 - libs/components/src/drawer/drawer.service.ts | 20 -- libs/components/src/layout/index.ts | 1 - .../src/layout/layout.component.html | 91 +++---- .../components/src/layout/layout.component.ts | 26 +- .../src/layout/scroll-layout.directive.ts | 35 --- .../dialog-virtual-scroll-block.component.ts | 12 +- .../components/kitchen-sink-main.component.ts | 133 +++++----- .../kitchen-sink/kitchen-sink.stories.ts | 30 ++- .../src/table/table-scroll.component.html | 2 +- .../src/table/table-scroll.component.ts | 3 - libs/components/src/table/table.mdx | 12 +- libs/components/src/table/table.stories.ts | 68 ++--- .../components/src/utils/has-scrolled-from.ts | 41 --- 36 files changed, 288 insertions(+), 649 deletions(-) delete mode 100644 libs/components/src/drawer/drawer.service.ts delete mode 100644 libs/components/src/layout/scroll-layout.directive.ts delete mode 100644 libs/components/src/utils/has-scrolled-from.ts diff --git a/apps/web/src/app/admin-console/organizations/manage/groups.component.html b/apps/web/src/app/admin-console/organizations/manage/groups.component.html index 4518513ba7..caae23f500 100644 --- a/apps/web/src/app/admin-console/organizations/manage/groups.component.html +++ b/apps/web/src/app/admin-console/organizations/manage/groups.component.html @@ -22,7 +22,7 @@

{{ "noGroupsInList" | i18n }}

- + diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.html b/apps/web/src/app/admin-console/organizations/members/members.component.html index 690e7aaa85..2162e33081 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.html +++ b/apps/web/src/app/admin-console/organizations/members/members.component.html @@ -67,7 +67,7 @@ - + diff --git a/apps/web/src/app/admin-console/organizations/members/members.module.ts b/apps/web/src/app/admin-console/organizations/members/members.module.ts index 98431758d2..81697f8c84 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.module.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.module.ts @@ -3,7 +3,6 @@ import { NgModule } from "@angular/core"; import { PasswordStrengthV2Component } from "@bitwarden/angular/tools/password-strength/password-strength-v2.component"; import { PasswordCalloutComponent } from "@bitwarden/auth/angular"; -import { ScrollLayoutDirective } from "@bitwarden/components"; import { LooseComponentsModule } from "../../../shared"; import { SharedOrganizationModule } from "../shared"; @@ -28,7 +27,6 @@ import { MembersComponent } from "./members.component"; PasswordCalloutComponent, ScrollingModule, PasswordStrengthV2Component, - ScrollLayoutDirective, ], declarations: [ BulkConfirmDialogComponent, diff --git a/apps/web/src/app/admin-console/organizations/organization.module.ts b/apps/web/src/app/admin-console/organizations/organization.module.ts index 687361760c..459948d0f1 100644 --- a/apps/web/src/app/admin-console/organizations/organization.module.ts +++ b/apps/web/src/app/admin-console/organizations/organization.module.ts @@ -1,8 +1,6 @@ import { ScrollingModule } from "@angular/cdk/scrolling"; import { NgModule } from "@angular/core"; -import { ScrollLayoutDirective } from "@bitwarden/components"; - import { LooseComponentsModule } from "../../shared"; import { CoreOrganizationModule } from "./core"; @@ -20,7 +18,6 @@ import { AccessSelectorModule } from "./shared/components/access-selector"; OrganizationsRoutingModule, LooseComponentsModule, ScrollingModule, - ScrollLayoutDirective, ], declarations: [GroupsComponent, GroupAddEditComponent], }) diff --git a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts index 9806f99d90..07bff3aba6 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts @@ -613,5 +613,5 @@ export function openCollectionDialog( dialogService: DialogService, config: DialogConfig>, ) { - return dialogService.open(CollectionDialogComponent, config); + return dialogService.open(CollectionDialogComponent, config); } diff --git a/apps/web/src/app/auth/settings/security/device-management.component.spec.ts b/apps/web/src/app/auth/settings/security/device-management.component.spec.ts index d86123f52b..84c1dfcb63 100644 --- a/apps/web/src/app/auth/settings/security/device-management.component.spec.ts +++ b/apps/web/src/app/auth/settings/security/device-management.component.spec.ts @@ -9,13 +9,7 @@ import { DeviceType } from "@bitwarden/common/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { MessageListener } from "@bitwarden/common/platform/messaging"; -import { - DialogService, - ToastService, - TableModule, - PopoverModule, - LayoutComponent, -} from "@bitwarden/components"; +import { DialogService, ToastService, TableModule, PopoverModule } from "@bitwarden/components"; import { SharedModule } from "../../../shared"; import { VaultBannersService } from "../../../vault/individual-vault/vault-banners/services/vault-banners.service"; @@ -121,12 +115,6 @@ describe("DeviceManagementComponent", () => { showError: jest.fn(), }, }, - { - provide: LayoutComponent, - useValue: { - mainContent: jest.fn(), - }, - }, ], }).compileComponents(); diff --git a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts index d0eb065b74..7e6c0d464c 100644 --- a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts +++ b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts @@ -1,3 +1,4 @@ +import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; import { AbstractControl, @@ -18,10 +19,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrgKey } from "@bitwarden/common/types/key"; import { - DialogRef, ButtonModule, - DialogConfig, - DIALOG_DATA, DialogModule, DialogService, FormFieldModule, diff --git a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts index da5d2b54f9..5b249683b5 100644 --- a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts +++ b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts @@ -1,3 +1,4 @@ +import { DialogRef } from "@angular/cdk/dialog"; import { formatDate } from "@angular/common"; import { Component, OnInit, signal } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; @@ -15,7 +16,7 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { StateProvider } from "@bitwarden/common/platform/state"; import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; -import { DialogRef, DialogService, ToastService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; import { AddSponsorshipDialogComponent } from "./add-sponsorship-dialog.component"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.html b/apps/web/src/app/vault/components/vault-items/vault-items.component.html index 7f3cc38712..f1f50beb58 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.html @@ -1,4 +1,4 @@ - + diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.module.ts b/apps/web/src/app/vault/components/vault-items/vault-items.module.ts index ab4f8bddb1..e54a9c1141 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.module.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { RouterModule } from "@angular/router"; -import { ScrollLayoutDirective, TableModule } from "@bitwarden/components"; +import { TableModule } from "@bitwarden/components"; import { CollectionNameBadgeComponent } from "../../../admin-console/organizations/collections"; import { GroupBadgeModule } from "../../../admin-console/organizations/collections/group-badge/group-badge.module"; @@ -26,7 +26,6 @@ import { VaultItemsComponent } from "./vault-items.component"; CollectionNameBadgeComponent, GroupBadgeModule, PipesModule, - ScrollLayoutDirective, ], declarations: [VaultItemsComponent, VaultCipherRowComponent, VaultCollectionRowComponent], exports: [VaultItemsComponent], diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts index 02c845ac94..55807ed855 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts @@ -2,13 +2,7 @@ // @ts-strict-ignore import { importProvidersFrom } from "@angular/core"; import { RouterModule } from "@angular/router"; -import { - applicationConfig, - componentWrapperDecorator, - Meta, - moduleMetadata, - StoryObj, -} from "@storybook/angular"; +import { applicationConfig, Meta, moduleMetadata, StoryObj } from "@storybook/angular"; import { BehaviorSubject, of } from "rxjs"; import { @@ -35,7 +29,6 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; -import { LayoutComponent } from "@bitwarden/components"; import { GroupView } from "../../../admin-console/organizations/core"; import { PreloadedEnglishI18nModule } from "../../../core/tests"; @@ -55,9 +48,8 @@ export default { title: "Web/Vault/Items", component: VaultItemsComponent, decorators: [ - componentWrapperDecorator((story) => `${story}`), moduleMetadata({ - imports: [VaultItemsModule, RouterModule, LayoutComponent], + imports: [VaultItemsModule, RouterModule], providers: [ { provide: EnvironmentService, diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.html index f203b7a934..66c4267844 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.html @@ -55,7 +55,7 @@ > {{ "providerUsersNeedConfirmed" | i18n }} - + diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts index 01f1facfc1..597acb0d4f 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts @@ -4,7 +4,7 @@ import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { CardComponent, ScrollLayoutDirective, SearchModule } from "@bitwarden/components"; +import { CardComponent, SearchModule } from "@bitwarden/components"; import { DangerZoneComponent } from "@bitwarden/web-vault/app/auth/settings/account/danger-zone.component"; import { OrganizationPlansComponent } from "@bitwarden/web-vault/app/billing"; import { PaymentComponent } from "@bitwarden/web-vault/app/billing/shared/payment/payment.component"; @@ -53,7 +53,6 @@ import { VerifyRecoverDeleteProviderComponent } from "./verify-recover-delete-pr ScrollingModule, VerifyBankAccountComponent, CardComponent, - ScrollLayoutDirective, PaymentComponent, ], declarations: [ diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts index c7d82c3ec0..e74682f64f 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts @@ -1,3 +1,4 @@ +import { BasePortalOutlet } from "@angular/cdk/portal"; import { Component, Inject, OnInit } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; @@ -32,7 +33,8 @@ export const openCreateClientDialog = ( dialogService: DialogService, dialogConfig: DialogConfig< CreateClientDialogParams, - DialogRef + DialogRef, + BasePortalOutlet >, ) => dialogService.open( diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index 5215ad0136..e7c5a17c30 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -1,17 +1,12 @@ import { DIALOG_DATA, DialogModule, DialogRef } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; -import { NoopAnimationsModule } from "@angular/platform-browser/animations"; -import { RouterTestingModule } from "@angular/router/testing"; -import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; -import { getAllByRole, userEvent } from "@storybook/test"; +import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ButtonModule } from "../button"; import { IconButtonModule } from "../icon-button"; -import { LayoutComponent } from "../layout"; import { SharedModule } from "../shared"; -import { positionFixedWrapperDecorator } from "../stories/storybook-decorators"; import { I18nMockService } from "../utils/i18n-mock.service"; import { DialogComponent } from "./dialog/dialog.component"; @@ -24,12 +19,7 @@ interface Animal { } @Component({ - template: ` - - - - - `, + template: ``, }) class StoryDialogComponent { constructor(public dialogService: DialogService) {} @@ -41,14 +31,6 @@ class StoryDialogComponent { }, }); } - - openDrawer() { - this.dialogService.openDrawer(StoryDialogContentComponent, { - data: { - animal: "panda", - }, - }); - } } @Component({ @@ -83,37 +65,25 @@ export default { title: "Component Library/Dialogs/Service", component: StoryDialogComponent, decorators: [ - positionFixedWrapperDecorator(), moduleMetadata({ declarations: [StoryDialogContentComponent], imports: [ SharedModule, ButtonModule, - NoopAnimationsModule, DialogModule, IconButtonModule, DialogCloseDirective, DialogComponent, DialogTitleContainerDirective, - RouterTestingModule, - LayoutComponent, ], - providers: [DialogService], - }), - applicationConfig({ providers: [ + DialogService, { provide: I18nService, useFactory: () => { return new I18nMockService({ close: "Close", - search: "Search", - skipToContent: "Skip to content", - submenu: "submenu", - toggleCollapse: "toggle collapse", - toggleSideNavigation: "Toggle side navigation", - yes: "Yes", - no: "No", + loading: "Loading", }); }, }, @@ -130,21 +100,4 @@ export default { type Story = StoryObj; -export const Default: Story = { - play: async (context) => { - const canvas = context.canvasElement; - - const button = getAllByRole(canvas, "button")[0]; - await userEvent.click(button); - }, -}; - -/** Drawers must be a descendant of `bit-layout`. */ -export const Drawer: Story = { - play: async (context) => { - const canvas = context.canvasElement; - - const button = getAllByRole(canvas, "button")[1]; - await userEvent.click(button); - }, -}; +export const Default: Story = {}; diff --git a/libs/components/src/dialog/dialog.service.ts b/libs/components/src/dialog/dialog.service.ts index 409bf0a5b5..83aaaff470 100644 --- a/libs/components/src/dialog/dialog.service.ts +++ b/libs/components/src/dialog/dialog.service.ts @@ -1,25 +1,31 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore import { - Dialog as CdkDialog, - DialogConfig as CdkDialogConfig, - DialogRef as CdkDialogRefBase, - DIALOG_DATA, - DialogCloseOptions, + DEFAULT_DIALOG_CONFIG, + Dialog, + DialogConfig, + DialogRef, + DIALOG_SCROLL_STRATEGY, } from "@angular/cdk/dialog"; -import { ComponentType, ScrollStrategy } from "@angular/cdk/overlay"; -import { ComponentPortal, Portal } from "@angular/cdk/portal"; -import { Injectable, Injector, TemplateRef, inject } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { ComponentType, Overlay, OverlayContainer, ScrollStrategy } from "@angular/cdk/overlay"; +import { + Inject, + Injectable, + Injector, + OnDestroy, + Optional, + SkipSelf, + TemplateRef, +} from "@angular/core"; import { NavigationEnd, Router } from "@angular/router"; -import { filter, firstValueFrom, map, Observable, Subject, switchMap } from "rxjs"; +import { filter, firstValueFrom, Subject, switchMap, takeUntil } from "rxjs"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { DrawerService } from "../drawer/drawer.service"; - import { SimpleConfigurableDialogComponent } from "./simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component"; -import { SimpleDialogOptions } from "./simple-dialog/types"; +import { SimpleDialogOptions, Translation } from "./simple-dialog/types"; /** * The default `BlockScrollStrategy` does not work well with virtual scrolling. @@ -42,163 +48,61 @@ class CustomBlockScrollStrategy implements ScrollStrategy { detach() {} } -export abstract class DialogRef - implements Pick, "close" | "closed" | "disableClose" | "componentInstance"> -{ - abstract readonly isDrawer?: boolean; - - // --- From CdkDialogRef --- - abstract close(result?: R, options?: DialogCloseOptions): void; - abstract readonly closed: Observable; - abstract disableClose: boolean | undefined; - /** - * @deprecated - * Does not work with drawer dialogs. - **/ - abstract componentInstance: C | null; -} - -export type DialogConfig = Pick< - CdkDialogConfig, - "data" | "disableClose" | "ariaModal" | "positionStrategy" | "height" | "width" ->; - -class DrawerDialogRef implements DialogRef { - readonly isDrawer = true; - - private _closed = new Subject(); - closed = this._closed.asObservable(); - disableClose = false; - - /** The portal containing the drawer */ - portal?: Portal; - - constructor(private drawerService: DrawerService) {} - - close(result?: R, _options?: DialogCloseOptions): void { - if (this.disableClose) { - return; - } - this.drawerService.close(this.portal!); - this._closed.next(result); - this._closed.complete(); - } - - componentInstance: C | null = null; -} - -/** - * DialogRef that delegates functionality to the CDK implementation - **/ -export class CdkDialogRef implements DialogRef { - readonly isDrawer = false; - - /** This is not available until after construction, @see DialogService.open. */ - cdkDialogRefBase!: CdkDialogRefBase; - - // --- Delegated to CdkDialogRefBase --- - - close(result?: R, options?: DialogCloseOptions): void { - this.cdkDialogRefBase.close(result, options); - } - - get closed(): Observable { - return this.cdkDialogRefBase.closed; - } - - get disableClose(): boolean | undefined { - return this.cdkDialogRefBase.disableClose; - } - set disableClose(value: boolean | undefined) { - this.cdkDialogRefBase.disableClose = value; - } - - // Delegate the `componentInstance` property to the CDK DialogRef - get componentInstance(): C | null { - return this.cdkDialogRefBase.componentInstance; - } -} - @Injectable() -export class DialogService { - private dialog = inject(CdkDialog); - private drawerService = inject(DrawerService); - private injector = inject(Injector); - private router = inject(Router, { optional: true }); - private authService = inject(AuthService, { optional: true }); - private i18nService = inject(I18nService); +export class DialogService extends Dialog implements OnDestroy { + private _destroy$ = new Subject(); private backDropClasses = ["tw-fixed", "tw-bg-black", "tw-bg-opacity-30", "tw-inset-0"]; - private defaultScrollStrategy = new CustomBlockScrollStrategy(); - private activeDrawer: DrawerDialogRef | null = null; - constructor() { - /** - * TODO: This logic should exist outside of `libs/components`. - * @see https://bitwarden.atlassian.net/browse/CL-657 - **/ + private defaultScrollStrategy = new CustomBlockScrollStrategy(); + + constructor( + /** Parent class constructor */ + _overlay: Overlay, + _injector: Injector, + @Optional() @Inject(DEFAULT_DIALOG_CONFIG) _defaultOptions: DialogConfig, + @Optional() @SkipSelf() _parentDialog: Dialog, + _overlayContainer: OverlayContainer, + @Inject(DIALOG_SCROLL_STRATEGY) scrollStrategy: any, + + /** Not in parent class */ + @Optional() router: Router, + @Optional() authService: AuthService, + + protected i18nService: I18nService, + ) { + super(_overlay, _injector, _defaultOptions, _parentDialog, _overlayContainer, scrollStrategy); + /** Close all open dialogs if the vault locks */ - if (this.router && this.authService) { - this.router.events + if (router && authService) { + router.events .pipe( filter((event) => event instanceof NavigationEnd), - switchMap(() => this.authService!.getAuthStatus()), + switchMap(() => authService.getAuthStatus()), filter((v) => v !== AuthenticationStatus.Unlocked), - takeUntilDestroyed(), + takeUntil(this._destroy$), ) .subscribe(() => this.closeAll()); } } - open( + override ngOnDestroy(): void { + this._destroy$.next(); + this._destroy$.complete(); + super.ngOnDestroy(); + } + + override open( componentOrTemplateRef: ComponentType | TemplateRef, config?: DialogConfig>, ): DialogRef { - /** - * This is a bit circular in nature: - * We need the DialogRef instance for the DI injector that is passed *to* `Dialog.open`, - * but we get the base CDK DialogRef instance *from* `Dialog.open`. - * - * To break the circle, we define CDKDialogRef as a wrapper for the CDKDialogRefBase. - * This allows us to create the class instance and provide the base instance later, almost like "deferred inheritance". - **/ - const ref = new CdkDialogRef(); - const injector = this.createInjector({ - data: config?.data, - dialogRef: ref, - }); - - // Merge the custom config with the default config - const _config = { + config = { backdropClass: this.backDropClasses, scrollStrategy: this.defaultScrollStrategy, - injector, ...config, }; - ref.cdkDialogRefBase = this.dialog.open(componentOrTemplateRef, _config); - return ref; - } - - /** Opens a dialog in the side drawer */ - openDrawer( - component: ComponentType, - config?: DialogConfig>, - ): DialogRef { - this.activeDrawer?.close(); - /** - * This is also circular. When creating the DrawerDialogRef, we do not yet have a portal instance to provide to the injector. - * Similar to `this.open`, we get around this with mutability. - */ - this.activeDrawer = new DrawerDialogRef(this.drawerService); - const portal = new ComponentPortal( - component, - null, - this.createInjector({ data: config?.data, dialogRef: this.activeDrawer }), - ); - this.activeDrawer.portal = portal; - this.drawerService.open(portal); - return this.activeDrawer; + return super.open(componentOrTemplateRef, config); } /** @@ -209,7 +113,8 @@ export class DialogService { */ async openSimpleDialog(simpleDialogOptions: SimpleDialogOptions): Promise { const dialogRef = this.openSimpleDialogRef(simpleDialogOptions); - return firstValueFrom(dialogRef.closed.pipe(map((v: boolean | undefined) => !!v))); + + return firstValueFrom(dialogRef.closed); } /** @@ -229,29 +134,20 @@ export class DialogService { }); } - /** Close all open dialogs */ - closeAll(): void { - return this.dialog.closeAll(); - } + protected translate(translation: string | Translation, defaultKey?: string): string { + if (translation == null && defaultKey == null) { + return null; + } - /** The injector that is passed to the opened dialog */ - private createInjector(opts: { data: unknown; dialogRef: DialogRef }): Injector { - return Injector.create({ - providers: [ - { - provide: DIALOG_DATA, - useValue: opts.data, - }, - { - provide: DialogRef, - useValue: opts.dialogRef, - }, - { - provide: CdkDialogRefBase, - useValue: opts.dialogRef, - }, - ], - parent: this.injector, - }); + if (translation == null) { + return this.i18nService.t(defaultKey); + } + + // Translation interface use implies we must localize. + if (typeof translation === "object") { + return this.i18nService.t(translation.key, ...(translation.placeholders ?? [])); + } + + return translation; } } diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index eaf7fc2bee..01f0598512 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -1,22 +1,12 @@ -@let isDrawer = dialogRef?.isDrawer;
- @let showHeaderBorder = !isDrawer || background === "alt" || bodyHasScrolledFrom().top;
-

} -

+ @@ -151,6 +90,72 @@ class KitchenSinkDialog { + + + + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ + What did foo say to bar? + + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+
+
`, }) export class KitchenSinkMainComponent { @@ -163,7 +168,7 @@ export class KitchenSinkMainComponent { } openDrawer() { - this.dialogService.openDrawer(KitchenSinkDialog); + this.drawerOpen.set(true); } navItems = [ diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts index d318e1b5f0..f57a9de4e6 100644 --- a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts +++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts @@ -14,6 +14,7 @@ import { import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { DialogService } from "../../dialog"; import { LayoutComponent } from "../../layout"; import { I18nMockService } from "../../utils/i18n-mock.service"; import { positionFixedWrapperDecorator } from "../storybook-decorators"; @@ -38,20 +39,8 @@ export default { KitchenSinkTable, KitchenSinkToggleList, ], - }), - applicationConfig({ providers: [ - provideNoopAnimations(), - importProvidersFrom( - RouterModule.forRoot( - [ - { path: "", redirectTo: "bitwarden", pathMatch: "full" }, - { path: "bitwarden", component: KitchenSinkMainComponent }, - { path: "virtual-scroll", component: DialogVirtualScrollBlockComponent }, - ], - { useHash: true }, - ), - ), + DialogService, { provide: I18nService, useFactory: () => { @@ -69,6 +58,21 @@ export default { }, ], }), + applicationConfig({ + providers: [ + provideNoopAnimations(), + importProvidersFrom( + RouterModule.forRoot( + [ + { path: "", redirectTo: "bitwarden", pathMatch: "full" }, + { path: "bitwarden", component: KitchenSinkMainComponent }, + { path: "virtual-scroll", component: DialogVirtualScrollBlockComponent }, + ], + { useHash: true }, + ), + ), + ], + }), ], } as Meta; diff --git a/libs/components/src/table/table-scroll.component.html b/libs/components/src/table/table-scroll.component.html index 523912cd7a..8f2c88ba3a 100644 --- a/libs/components/src/table/table-scroll.component.html +++ b/libs/components/src/table/table-scroll.component.html @@ -1,5 +1,5 @@ diff --git a/libs/components/src/table/table-scroll.component.ts b/libs/components/src/table/table-scroll.component.ts index e83dbbecc6..9d81e3ffe8 100644 --- a/libs/components/src/table/table-scroll.component.ts +++ b/libs/components/src/table/table-scroll.component.ts @@ -20,8 +20,6 @@ import { TrackByFunction, } from "@angular/core"; -import { ScrollLayoutDirective } from "../layout"; - import { RowDirective } from "./row.directive"; import { TableComponent } from "./table.component"; @@ -58,7 +56,6 @@ export class BitRowDef { CdkFixedSizeVirtualScroll, CdkVirtualForOf, RowDirective, - ScrollLayoutDirective, ], }) export class TableScrollComponent diff --git a/libs/components/src/table/table.mdx b/libs/components/src/table/table.mdx index 59bf5b773a..8d784190ed 100644 --- a/libs/components/src/table/table.mdx +++ b/libs/components/src/table/table.mdx @@ -142,7 +142,7 @@ dataSource.filter = (data) => data.orgType === "family"; Rudimentary string filtering is supported out of the box with `TableDataSource.simpleStringFilter`. It works by converting each entry into a string of it's properties. The provided string is then -compared against the filter value using a simple `indexOf` check. For convenience, you can also just +compared against the filter value using a simple `indexOf` check. For convienence, you can also just pass a string directly. ```ts @@ -153,7 +153,7 @@ dataSource.filter = "search value"; ### Virtual Scrolling -It's heavily advised to use virtual scrolling if you expect the table to have any significant amount +It's heavily adviced to use virtual scrolling if you expect the table to have any significant amount of data. This is done by using the `bit-table-scroll` component instead of the `bit-table` component. This component behaves slightly different from the `bit-table` component. Instead of using the `*ngFor` directive to render the rows, you provide a `bitRowDef` template that will be @@ -178,14 +178,6 @@ height and align vertically. ``` -#### Deprecated approach - -Before `bit-table-scroll` was introduced, virtual scroll in tables was implemented manually via -constructs from Angular CDK. This included wrapping the table with a `cdk-virtual-scroll-viewport` -and targeting with `bit-layout`'s scroll container with the `bitScrollLayout` directive. - -This pattern is deprecated in favor of `bit-table-scroll`. - ## Accessibility - Always include a row or column header with your table; this allows assistive technology to better diff --git a/libs/components/src/table/table.stories.ts b/libs/components/src/table/table.stories.ts index d696e6077d..e8ab24ee8b 100644 --- a/libs/components/src/table/table.stories.ts +++ b/libs/components/src/table/table.stories.ts @@ -1,13 +1,6 @@ -import { RouterTestingModule } from "@angular/router/testing"; import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; - import { countries } from "../form/countries"; -import { LayoutComponent } from "../layout"; -import { mockLayoutI18n } from "../layout/mocks"; -import { positionFixedWrapperDecorator } from "../stories/storybook-decorators"; -import { I18nMockService } from "../utils"; import { TableDataSource } from "./table-data-source"; import { TableModule } from "./table.module"; @@ -15,17 +8,8 @@ import { TableModule } from "./table.module"; export default { title: "Component Library/Table", decorators: [ - positionFixedWrapperDecorator(), moduleMetadata({ - imports: [TableModule, LayoutComponent, RouterTestingModule], - providers: [ - { - provide: I18nService, - useFactory: () => { - return new I18nMockService(mockLayoutI18n); - }, - }, - ], + imports: [TableModule], }), ], argTypes: { @@ -132,20 +116,18 @@ export const Scrollable: Story = { trackBy: (index: number, item: any) => item.id, }, template: ` - - - - Id - Name - Other - - - {{ row.id }} - {{ row.name }} - {{ row.other }} - - - + + + Id + Name + Other + + + {{ row.id }} + {{ row.name }} + {{ row.other }} + + `, }), }; @@ -162,19 +144,17 @@ export const Filterable: Story = { sortFn: (a: any, b: any) => a.id - b.id, }, template: ` - - - - - Name - Value - - - {{ row.name }} - {{ row.value }} - - - + + + + Name + Value + + + {{ row.name }} + {{ row.value }} + + `, }), }; diff --git a/libs/components/src/utils/has-scrolled-from.ts b/libs/components/src/utils/has-scrolled-from.ts deleted file mode 100644 index 44c73465bd..0000000000 --- a/libs/components/src/utils/has-scrolled-from.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { CdkScrollable } from "@angular/cdk/scrolling"; -import { Signal, inject, signal } from "@angular/core"; -import { toObservable, toSignal } from "@angular/core/rxjs-interop"; -import { map, startWith, switchMap } from "rxjs"; - -export type ScrollState = { - /** `true` when the scrollbar is not at the top-most position */ - top: boolean; - - /** `true` when the scrollbar is not at the bottom-most position */ - bottom: boolean; -}; - -/** - * Check if a `CdkScrollable` instance has been scrolled - * @param scrollable The instance to check, defaults to the one provided by the current injector - * @returns {Signal} - */ -export const hasScrolledFrom = (scrollable?: Signal): Signal => { - const _scrollable = scrollable ?? signal(inject(CdkScrollable)); - const scrollable$ = toObservable(_scrollable); - - const scrollState$ = scrollable$.pipe( - switchMap((_scrollable) => - _scrollable.elementScrolled().pipe( - startWith(null), - map(() => ({ - top: _scrollable.measureScrollOffset("top") > 0, - bottom: _scrollable.measureScrollOffset("bottom") > 0, - })), - ), - ), - ); - - return toSignal(scrollState$, { - initialValue: { - top: false, - bottom: false, - }, - }); -};