diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 187f500828c..4b956fd577a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -131,6 +131,7 @@ apps/web/src/translation-constants.ts @bitwarden/team-platform-dev .github/workflows/version-auto-bump.yml @bitwarden/team-platform-dev # ESLint custom rules libs/eslint @bitwarden/team-platform-dev +libs/eslint/components @bitwarden/team-ui-foundation # Typescript tooling tsconfig.base.json @bitwarden/team-platform-dev nx.json @bitwarden/team-platform-dev diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 906bbbd7125..e646049c3d6 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -197,6 +197,7 @@ "nx", "oo7", "oslog", + "parse5", "pin-project", "pkg", "postcss", diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index fc708ba9790..b33378458d1 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4134,15 +4134,11 @@ "selectImportCollection": { "message": "Select a collection" }, - "importTargetHint": { - "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", - "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", - "placeholders": { - "destination": { - "content": "$1", - "example": "folder or collection" - } - } + "importTargetHintCollection": { + "message": "Select this option if you want the imported file contents moved to a collection" + }, + "importTargetHintFolder": { + "message": "Select this option if you want the imported file contents moved to a folder" }, "importUnassignedItemsError": { "message": "File contains unassigned items." @@ -5582,5 +5578,9 @@ }, "showLess": { "message": "Show less" + }, + "moreBreadcrumbs": { + "message": "More breadcrumbs", + "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." } } diff --git a/apps/browser/src/autofill/popup/settings/blocked-domains.component.html b/apps/browser/src/autofill/popup/settings/blocked-domains.component.html index 8156525301b..6a08b4483af 100644 --- a/apps/browser/src/autofill/popup/settings/blocked-domains.component.html +++ b/apps/browser/src/autofill/popup/settings/blocked-domains.component.html @@ -30,7 +30,7 @@ diff --git a/apps/browser/src/platform/popup/layout/popup-header.component.html b/apps/browser/src/platform/popup/layout/popup-header.component.html index 014ebc86411..2aac161b9d5 100644 --- a/apps/browser/src/platform/popup/layout/popup-header.component.html +++ b/apps/browser/src/platform/popup/layout/popup-header.component.html @@ -19,8 +19,7 @@ bitIconButton="bwi-angle-left" type="button" *ngIf="showBackButton" - [title]="'back' | i18n" - [attr.aria-label]="'back' | i18n" + [label]="'back' | i18n" [bitAction]="backAction" >

diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index aeeed6f65ce..7455921b08b 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -67,14 +67,10 @@ class ExtensionPoppedContainerComponent {} - + - + @@ -102,13 +98,7 @@ class MockAddButtonComponent {} @Component({ selector: "mock-popout-button", template: ` - + `, imports: [IconButtonModule], }) @@ -278,7 +268,13 @@ class MockSettingsPageComponent {} - + `, @@ -671,17 +667,13 @@ export const WithVirtualScrollChild: Story = { - + diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts index f75e9cc29a5..f9a4221b3ef 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts @@ -150,7 +150,7 @@ describe("Browser Utils Service", () => { callback(undefined); }); - const isViewOpen = await browserPlatformUtilsService.isViewOpen(); + const isViewOpen = await browserPlatformUtilsService.isPopupOpen(); expect(isViewOpen).toBe(false); }); @@ -160,7 +160,7 @@ describe("Browser Utils Service", () => { callback(message.command === "checkVaultPopupHeartbeat"); }); - const isViewOpen = await browserPlatformUtilsService.isViewOpen(); + const isViewOpen = await browserPlatformUtilsService.isPopupOpen(); expect(isViewOpen).toBe(true); }); @@ -173,7 +173,7 @@ describe("Browser Utils Service", () => { callback(undefined); }); - const isViewOpen = await browserPlatformUtilsService.isViewOpen(); + const isViewOpen = await browserPlatformUtilsService.isPopupOpen(); expect(isViewOpen).toBe(false); diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts index beac7616d8d..4c5869054b2 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts @@ -150,7 +150,7 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic * message to the popup and waiting for a response. If a response is received, * the view is open. */ - async isViewOpen(): Promise { + async isPopupOpen(): Promise { if (this.isSafari()) { // Query views on safari since chrome.runtime.sendMessage does not timeout and will hang. return BrowserApi.isPopupOpen(); diff --git a/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts b/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts new file mode 100644 index 00000000000..26b49515b82 --- /dev/null +++ b/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts @@ -0,0 +1,60 @@ +import { map, merge, Observable } from "rxjs"; + +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { + ButtonLocation, + SystemNotificationClearInfo, + SystemNotificationCreateInfo, + SystemNotificationEvent, + SystemNotificationsService, +} from "@bitwarden/common/platform/system-notifications/system-notifications.service"; + +import { fromChromeEvent } from "../browser/from-chrome-event"; + +export class BrowserSystemNotificationService implements SystemNotificationsService { + notificationClicked$: Observable; + + constructor( + private logService: LogService, + private platformUtilsService: PlatformUtilsService, + ) { + this.notificationClicked$ = merge( + fromChromeEvent(chrome.notifications.onButtonClicked).pipe( + map(([notificationId, buttonIndex]) => ({ + id: notificationId, + buttonIdentifier: buttonIndex, + })), + ), + fromChromeEvent(chrome.notifications.onClicked).pipe( + map(([notificationId]: [string]) => ({ + id: notificationId, + buttonIdentifier: ButtonLocation.NotificationButton, + })), + ), + ); + } + + async create(createInfo: SystemNotificationCreateInfo): Promise { + return new Promise((resolve) => { + chrome.notifications.create( + { + iconUrl: chrome.runtime.getURL("images/icon128.png"), + message: createInfo.body, + type: "basic", + title: createInfo.title, + buttons: createInfo.buttons.map((value) => ({ title: value.title })), + }, + (notificationId) => resolve(notificationId), + ); + }); + } + + async clear(clearInfo: SystemNotificationClearInfo): Promise { + chrome.notifications.clear(clearInfo.id); + } + + isSupported(): boolean { + return "notifications" in chrome; + } +} diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index f531ebd5ca7..358ed33408c 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -92,12 +92,13 @@ import { AbstractStorageService, ObservableStorageService, } from "@bitwarden/common/platform/abstractions/storage.service"; +import { ActionsService } from "@bitwarden/common/platform/actions"; import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; // eslint-disable-next-line no-restricted-imports -- Used for dependency injection import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; import { flagEnabled } from "@bitwarden/common/platform/misc/flags"; -import { NotificationsService } from "@bitwarden/common/platform/notifications"; import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling"; +import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; @@ -113,6 +114,7 @@ import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/imp import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service"; import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service"; import { SyncService } from "@bitwarden/common/platform/sync"; +import { SystemNotificationsService } from "@bitwarden/common/platform/system-notifications/system-notifications.service"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -160,13 +162,14 @@ import { InlineMenuFieldQualificationService } from "../../autofill/services/inl import { ForegroundBrowserBiometricsService } from "../../key-management/biometrics/foreground-browser-biometrics"; import { ExtensionLockComponentService } from "../../key-management/lock/services/extension-lock-component.service"; import { ForegroundVaultTimeoutService } from "../../key-management/vault-timeout/foreground-vault-timeout.service"; +import { BrowserActionsService } from "../../platform/actions/browser-actions.service"; import { BrowserApi } from "../../platform/browser/browser-api"; import { runInsideAngular } from "../../platform/browser/run-inside-angular.operator"; /* eslint-disable no-restricted-imports */ import { ZonedMessageListenerService } from "../../platform/browser/zoned-message-listener.service"; import { ChromeMessageSender } from "../../platform/messaging/chrome-message.sender"; /* eslint-enable no-restricted-imports */ -import { ForegroundNotificationsService } from "../../platform/notifications/foreground-notifications.service"; +import { ForegroundServerNotificationsService } from "../../platform/notifications/foreground-server-notifications.service"; import { OffscreenDocumentService } from "../../platform/offscreen-document/abstractions/offscreen-document"; import { DefaultOffscreenDocumentService } from "../../platform/offscreen-document/offscreen-document.service"; import { PopupCompactModeService } from "../../platform/popup/layout/popup-compact-mode.service"; @@ -184,6 +187,7 @@ import { ForegroundTaskSchedulerService } from "../../platform/services/task-sch import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider"; import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service"; import { ForegroundSyncService } from "../../platform/sync/foreground-sync.service"; +import { BrowserSystemNotificationService } from "../../platform/system-notifications/browser-system-notification.service"; import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging"; import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service"; import { Fido2UserVerificationService } from "../../vault/services/fido2-user-verification.service"; @@ -253,6 +257,11 @@ const safeProviders: SafeProvider[] = [ }, deps: [GlobalStateProvider], }), + safeProvider({ + provide: ActionsService, + useClass: BrowserActionsService, + deps: [LogService, PlatformUtilsService], + }), safeProvider({ provide: KeyService, useFactory: ( @@ -609,6 +618,11 @@ const safeProviders: SafeProvider[] = [ useClass: SsoUrlService, deps: [], }), + safeProvider({ + provide: SystemNotificationsService, + useClass: BrowserSystemNotificationService, + deps: [LogService, PlatformUtilsService], + }), safeProvider({ provide: LoginComponentService, useClass: ExtensionLoginComponentService, @@ -674,8 +688,8 @@ const safeProviders: SafeProvider[] = [ deps: [KeyService, MasterPasswordApiService, InternalMasterPasswordServiceAbstraction, WINDOW], }), safeProvider({ - provide: NotificationsService, - useClass: ForegroundNotificationsService, + provide: ServerNotificationsService, + useClass: ForegroundServerNotificationsService, deps: [LogService], }), safeProvider({ diff --git a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html index 5d313188d8f..c6ea52aff62 100644 --- a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html +++ b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html @@ -26,7 +26,7 @@ slot="end" bitIconButton="bwi-trash" [bitAction]="deleteSend" - appA11yTitle="{{ 'delete' | i18n }}" + label="{{ 'delete' | i18n }}" > diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html index 21b298fb30a..8f184c6a0c1 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html @@ -38,7 +38,7 @@ type="button" buttonType="danger" bitIconButton="bwi-trash" - [appA11yTitle]="'delete' | i18n" + [label]="'delete' | i18n" > diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html index 567d5277454..f4cc27171ad 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html @@ -7,7 +7,7 @@ size="small" appCopyField="username" [cipher]="cipher" - [appA11yTitle]="'copyUsername' | i18n" + [label]="'copyUsername' | i18n" > @@ -18,7 +18,7 @@ size="small" appCopyField="password" [cipher]="cipher" - [appA11yTitle]="'copyPassword' | i18n" + [label]="'copyPassword' | i18n" > @@ -28,7 +28,7 @@ size="small" appCopyField="totp" [cipher]="cipher" - [appA11yTitle]="'copyVerificationCode' | i18n" + [label]="'copyVerificationCode' | i18n" > @@ -40,7 +40,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]="'copyFieldCipherName' | i18n: singleCopyableLogin.key : cipher.name" + [label]="'copyFieldCipherName' | i18n: singleCopyableLogin.key : cipher.name" [appCopyField]="singleCopyableLogin.field" [cipher]="cipher" > @@ -49,7 +49,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]=" + [label]=" hasLoginValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n) " [disabled]="!hasLoginValues" @@ -86,7 +86,7 @@ size="small" appCopyField="cardNumber" [cipher]="cipher" - [appA11yTitle]="'copyNumber' | i18n" + [label]="'copyNumber' | i18n" > @@ -96,7 +96,7 @@ size="small" appCopyField="securityCode" [cipher]="cipher" - [appA11yTitle]="'copySecurityCode' | i18n" + [label]="'copySecurityCode' | i18n" > @@ -107,7 +107,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]="'copyFieldCipherName' | i18n: singleCopyableCard.key : cipher.name" + [label]="'copyFieldCipherName' | i18n: singleCopyableCard.key : cipher.name" [appCopyField]="singleCopyableCard.field" [cipher]="cipher" showToast @@ -117,7 +117,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]=" + [label]=" hasCardValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n) " [disabled]="!hasCardValues" @@ -142,7 +142,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]="'copyFieldCipherName' | i18n: singleCopyableIdentity.key : cipher.name" + [label]="'copyFieldCipherName' | i18n: singleCopyableIdentity.key : cipher.name" [appCopyField]="singleCopyableIdentity.field" [cipher]="cipher" showToast @@ -152,7 +152,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]=" + [label]=" hasIdentityValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n) " [disabled]="!hasIdentityValues" @@ -180,9 +180,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]=" - hasSecureNoteValue ? ('copyNoteTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n) - " + [label]="hasSecureNoteValue ? ('copyNoteTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n)" appCopyField="secureNote" [cipher]="cipher" > @@ -193,9 +191,7 @@ type="button" bitIconButton="bwi-clone" size="small" - [appA11yTitle]=" - hasSshKeyValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n) - " + [label]="hasSshKeyValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n)" [disabled]="!hasSshKeyValues" [bitMenuTriggerFor]="sshKeyOptions" > diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html index 962f0c914f5..42e2779679a 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html @@ -3,8 +3,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - [attr.aria-label]="'moreOptionsLabel' | i18n: cipher.name" - [title]="'moreOptionsTitle' | i18n: cipher.name" + [label]="'moreOptionsLabel' | i18n: cipher.name" [disabled]="decryptionFailure" [bitMenuTriggerFor]="moreOptions" > diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html index 91feaa433a9..1ab162b56fb 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html @@ -8,7 +8,7 @@ bitIconButton="bwi-sliders" [buttonType]="'muted'" [bitDisclosureTriggerFor]="disclosureRef" - [appA11yTitle]="'filterVault' | i18n" + [label]="'filterVault' | i18n" aria-describedby="filters-applied" >

diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html index 8c76db600ae..9b8380a4214 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html @@ -33,7 +33,7 @@ type="button" buttonType="danger" bitIconButton="bwi-trash" - [appA11yTitle]="(cipher.isDeleted ? 'deleteForever' : 'delete') | i18n" + [label]="(cipher.isDeleted ? 'deleteForever' : 'delete') | i18n" > diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.html b/apps/browser/src/vault/popup/settings/folders-v2.component.html index 8cea05f9c17..b36b5affc23 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.html +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.html @@ -25,7 +25,7 @@ slot="end" type="button" (click)="openAddEditFolderDialog(folder)" - [appA11yTitle]="'editFolderWithName' | i18n: folder.name" + [label]="'editFolderWithName' | i18n: folder.name" bitIconButton="bwi-pencil-square" class="tw-self-end" data-testid="edit-folder-button" diff --git a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html index 11ed2674178..d1e70390844 100644 --- a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html +++ b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html @@ -37,8 +37,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - [attr.aria-label]="'moreOptionsLabel' | i18n: cipher.name" - [title]="'moreOptionsTitle' | i18n: cipher.name" + [label]="'moreOptionsLabel' | i18n: cipher.name" [bitMenuTriggerFor]="moreOptions" > diff --git a/apps/cli/src/platform/services/cli-platform-utils.service.ts b/apps/cli/src/platform/services/cli-platform-utils.service.ts index 7bed495bbf5..9e02b9dcca9 100644 --- a/apps/cli/src/platform/services/cli-platform-utils.service.ts +++ b/apps/cli/src/platform/services/cli-platform-utils.service.ts @@ -75,7 +75,7 @@ export class CliPlatformUtilsService implements PlatformUtilsService { return false; } - isViewOpen() { + isPopupOpen() { return Promise.resolve(false); } diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 37650c08b95..42eb7017e03 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2025.8.0", + "version": "2025.8.1", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 04651ed0c10..197290cf690 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -62,7 +62,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SystemService } from "@bitwarden/common/platform/abstractions/system.service"; -import { NotificationsService } from "@bitwarden/common/platform/notifications"; +import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications"; import { StateEventRunnerService } from "@bitwarden/common/platform/state"; import { SyncService } from "@bitwarden/common/platform/sync"; import { UserId } from "@bitwarden/common/types/guid"; @@ -155,7 +155,7 @@ export class AppComponent implements OnInit, OnDestroy { private messagingService: MessagingService, private collectionService: CollectionService, private searchService: SearchService, - private notificationsService: NotificationsService, + private notificationsService: ServerNotificationsService, private platformUtilsService: PlatformUtilsService, private systemService: SystemService, private processReloadService: ProcessReloadServiceAbstraction, diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 2c68821b6c7..6b511ff366d 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -13,7 +13,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; -import { NotificationsService } from "@bitwarden/common/platform/notifications"; +import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; @@ -38,7 +38,7 @@ export class InitService { private i18nService: I18nServiceAbstraction, private eventUploadService: EventUploadServiceAbstraction, private twoFactorService: TwoFactorServiceAbstraction, - private notificationsService: NotificationsService, + private notificationsService: ServerNotificationsService, private platformUtilsService: PlatformUtilsServiceAbstraction, private stateService: StateServiceAbstraction, private keyService: KeyServiceAbstraction, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index e190ad8c5ae..276c0c435f2 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -3494,15 +3494,11 @@ "selectImportCollection": { "message": "Select a collection" }, - "importTargetHint": { - "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", - "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", - "placeholders": { - "destination": { - "content": "$1", - "example": "folder or collection" - } - } + "importTargetHintCollection": { + "message": "Select this option if you want the imported file contents moved to a collection" + }, + "importTargetHintFolder": { + "message": "Select this option if you want the imported file contents moved to a folder" }, "importUnassignedItemsError": { "message": "File contains unassigned items." @@ -4080,5 +4076,9 @@ }, "enableAutotypeDescription": { "message": "Bitwarden does not validate input locations, be sure you are in the right window and field before using the shortcut." + }, + "moreBreadcrumbs": { + "message": "More breadcrumbs", + "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." } } diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 44ee6ec862d..6daff35e115 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.8.0", + "version": "2025.8.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.8.0", + "version": "2025.8.1", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index ffe70bb7bd7..ea2e8affda2 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.8.0", + "version": "2025.8.1", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/platform/services/electron-platform-utils.service.ts b/apps/desktop/src/platform/services/electron-platform-utils.service.ts index 43b867b7a68..23fb29e932a 100644 --- a/apps/desktop/src/platform/services/electron-platform-utils.service.ts +++ b/apps/desktop/src/platform/services/electron-platform-utils.service.ts @@ -59,7 +59,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { return ipc.platform.isMacAppStore; } - isViewOpen(): Promise { + isPopupOpen(): Promise { return Promise.resolve(false); } diff --git a/apps/web/src/404.html b/apps/web/src/404.html index 1a01aee40c7..b3b5a5713ca 100644 --- a/apps/web/src/404.html +++ b/apps/web/src/404.html @@ -15,23 +15,206 @@ - - Bitwarden + + Bitwarden +

+
+

Sorry, this page isn't available.

-
-

Sorry, this page isn't available.

+

+ The link you followed may be broken, or the page may have been removed. Try going back to + the previous page or see our + Help Center + for more information. +

+ Go to your web vault +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + `; +
-

- The link you followed may be broken, or the page may have been removed. Try going back to - the previous page or see our - Help Center for - more information. -

- - Go to your web vault -
-
- -
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/apps/web/src/app/admin-console/common/base.events.component.ts b/apps/web/src/app/admin-console/common/base.events.component.ts index 9d06be92eb8..ba315dee7fb 100644 --- a/apps/web/src/app/admin-console/common/base.events.component.ts +++ b/apps/web/src/app/admin-console/common/base.events.component.ts @@ -1,8 +1,13 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Directive } from "@angular/core"; +import { Directive, OnDestroy } from "@angular/core"; import { FormControl, FormGroup } from "@angular/forms"; +import { ActivatedRoute } from "@angular/router"; +import { combineLatest, filter, map, Observable, Subject, switchMap, takeUntil } from "rxjs"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EventResponse } from "@bitwarden/common/models/response/event.response"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { EventView } from "@bitwarden/common/models/view/event.view"; @@ -12,16 +17,17 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ToastService } from "@bitwarden/components"; -import { EventService } from "../../core"; +import { EventOptions, EventService } from "../../core"; import { EventExportService } from "../../tools/event-export"; @Directive() -export abstract class BaseEventsComponent { +export abstract class BaseEventsComponent implements OnDestroy { loading = true; loaded = false; events: EventView[]; dirtyDates = true; continuationToken: string; + canUseSM = false; abstract readonly exportFileName: string; @@ -30,6 +36,15 @@ export abstract class BaseEventsComponent { end: new FormControl(null), }); + protected canUseSM$: Observable; + protected activeOrganization$: Observable; + protected organizations$: Observable; + private destroySubject$ = new Subject(); + + protected get destroy$(): Observable { + return this.destroySubject$.asObservable(); + } + constructor( protected eventService: EventService, protected i18nService: I18nService, @@ -38,12 +53,39 @@ export abstract class BaseEventsComponent { protected logService: LogService, protected fileDownloadService: FileDownloadService, private toastService: ToastService, + protected activeRoute: ActivatedRoute, + protected accountService: AccountService, + protected organizationService: OrganizationService, ) { const defaultDates = this.eventService.getDefaultDateFilters(); this.start = defaultDates[0]; this.end = defaultDates[1]; } + protected initBase(): void { + this.organizations$ = this.accountService.activeAccount$.pipe( + filter((account): account is Account => !!account?.id), + switchMap((account) => this.organizationService.organizations$(account.id)), + ); + + this.activeOrganization$ = combineLatest([this.activeRoute.paramMap, this.organizations$]).pipe( + map(([params, orgs]) => orgs.find((org) => org.id === params.get("organizationId"))), + ); + + this.canUseSM$ = this.activeOrganization$.pipe( + map((org) => org?.canAccessSecretsManager ?? false), + ); + + this.canUseSM$.pipe(takeUntil(this.destroy$)).subscribe((value) => { + this.canUseSM = value; + }); + } + + ngOnDestroy(): void { + this.destroySubject$.next(); + this.destroySubject$.complete(); + } + get start(): string { return this.eventsForm.value.start; } @@ -139,7 +181,10 @@ export abstract class BaseEventsComponent { const events = await Promise.all( response.data.map(async (r) => { const userId = r.actingUserId == null ? r.userId : r.actingUserId; - const eventInfo = await this.eventService.getEventInfo(r); + const options = new EventOptions(); + options.disableLink = !this.canUseSM; + + const eventInfo = await this.eventService.getEventInfo(r, options); const user = this.getUserName(r, userId); const userName = user != null ? user.name : this.i18nService.t("unknown"); diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html index 50d34227b56..9c3e607d6eb 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html @@ -34,6 +34,7 @@ [bitMenuTriggerFor]="editCollectionMenu" size="small" type="button" + [label]="'editCollection' | i18n" > diff --git a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts index 10f68695e88..8484b05283d 100644 --- a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts @@ -8,6 +8,8 @@ import { firstValueFrom, switchMap } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EventResponse } from "@bitwarden/common/models/response/event.response"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { EventView } from "@bitwarden/common/models/view/event.view"; @@ -26,7 +28,7 @@ import { EventService } from "../../../core"; import { SharedModule } from "../../../shared"; export interface EntityEventsDialogParams { - entity: "user" | "cipher"; + entity: "user" | "cipher" | "secret" | "project"; entityId: string; organizationId?: string; @@ -72,6 +74,8 @@ export class EntityEventsComponent implements OnInit, OnDestroy { private toastService: ToastService, private router: Router, private activeRoute: ActivatedRoute, + private accountService: AccountService, + protected organizationService: OrganizationService, ) {} async ngOnInit() { @@ -162,6 +166,22 @@ export class EntityEventsComponent implements OnInit, OnDestroy { dates[1], clearExisting ? null : this.continuationToken, ); + } else if (this.params.entity === "secret") { + response = await this.apiService.getEventsSecret( + this.params.organizationId, + this.params.entityId, + dates[0], + dates[1], + clearExisting ? null : this.continuationToken, + ); + } else if (this.params.entity === "project") { + response = await this.apiService.getEventsProject( + this.params.organizationId, + this.params.entityId, + dates[0], + dates[1], + clearExisting ? null : this.continuationToken, + ); } else { response = await this.apiService.getEventsCipher( this.params.entityId, diff --git a/apps/web/src/app/admin-console/organizations/manage/events.component.ts b/apps/web/src/app/admin-console/organizations/manage/events.component.ts index 07f6be7d7f6..f442d429767 100644 --- a/apps/web/src/app/admin-console/organizations/manage/events.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/events.component.ts @@ -1,8 +1,8 @@ // 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 { ActivatedRoute, Router } from "@angular/router"; -import { concatMap, firstValueFrom, lastValueFrom, Subject, takeUntil } from "rxjs"; +import { ActivatedRoute } from "@angular/router"; +import { concatMap, firstValueFrom, lastValueFrom, takeUntil } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; @@ -60,8 +60,6 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe placeholderEvents = placeholderEvents as EventView[]; private orgUsersUserIdMap = new Map(); - private destroy$ = new Subject(); - readonly ProductTierType = ProductTierType; protected isBreadcrumbEventLogsEnabled$ = this.configService.getFeatureFlag$( @@ -75,18 +73,18 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe i18nService: I18nService, exportService: EventExportService, platformUtilsService: PlatformUtilsService, - private router: Router, logService: LogService, private userNamePipe: UserNamePipe, - private organizationService: OrganizationService, + protected organizationService: OrganizationService, private organizationUserApiService: OrganizationUserApiService, private organizationApiService: OrganizationApiServiceAbstraction, private providerService: ProviderService, fileDownloadService: FileDownloadService, toastService: ToastService, - private accountService: AccountService, + protected accountService: AccountService, private dialogService: DialogService, private configService: ConfigService, + protected activeRoute: ActivatedRoute, ) { super( eventService, @@ -96,10 +94,15 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe logService, fileDownloadService, toastService, + activeRoute, + accountService, + organizationService, ); } async ngOnInit() { + this.initBase(); + const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); this.route.params .pipe( @@ -233,9 +236,4 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe } await this.load(); } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } } diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html index 101512dea04..cc90d10fb4a 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html @@ -80,7 +80,7 @@ bitIconButton="bwi-trash" bitFormButton [bitAction]="delete" - [appA11yTitle]="'delete' | i18n" + [label]="'delete' | i18n" > 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 4518513ba7d..62d0b5b874b 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 @@ -46,7 +46,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > @@ -82,7 +82,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | 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 49946806efc..7e0aa465bf3 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 @@ -106,7 +106,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" *ngIf="showUserManagementControls$ | async" > @@ -350,7 +350,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts index a803f6ef7b5..5cb61197b99 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts @@ -51,7 +51,7 @@ const render: Story["render"] = (args) => ({ buttonType="danger" size="default" title="Delete" - aria-label="Delete"> + label="Delete"> `, diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html index e0ffc9a4bce..116af15f579 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html @@ -122,7 +122,7 @@ type="button" bitIconButton="bwi-close" buttonType="muted" - appA11yTitle="{{ 'remove' | i18n }} {{ item.labelName }}" + label="{{ 'remove' | i18n }} {{ item.labelName }}" [disabled]="disabled" (click)="selectionList.deselectItem(item.id); handleBlur()" > diff --git a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.html b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.html index 4a91fcc2a41..dec257b3741 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.html +++ b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.html @@ -143,7 +143,7 @@ buttonType="danger" class="tw-ml-auto" bitFormButton - [appA11yTitle]="'delete' | i18n" + [label]="'delete' | i18n" [bitAction]="delete" [disabled]="loading" > diff --git a/apps/web/src/app/app.component.html b/apps/web/src/app/app.component.html index 471d89be1c1..638ad47fe4e 100644 --- a/apps/web/src/app/app.component.html +++ b/apps/web/src/app/app.component.html @@ -2,8 +2,10 @@ -
- Bitwarden +
+
+ Bitwarden +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + `; +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index ae20670c2dd..1cb95250611 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -22,7 +22,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { NotificationsService } from "@bitwarden/common/platform/notifications"; +import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications"; import { StateEventRunnerService } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -76,7 +76,7 @@ export class AppComponent implements OnDestroy, OnInit { private keyService: KeyService, private collectionService: CollectionService, private searchService: SearchService, - private notificationsService: NotificationsService, + private serverNotificationsService: ServerNotificationsService, private stateService: StateService, private eventUploadService: EventUploadService, protected policyListService: PolicyListService, @@ -88,14 +88,14 @@ export class AppComponent implements OnDestroy, OnInit { private accountService: AccountService, private processReloadService: ProcessReloadServiceAbstraction, private deviceTrustToastService: DeviceTrustToastService, - private readonly destoryRef: DestroyRef, + private readonly destroy: DestroyRef, private readonly documentLangSetter: DocumentLangSetter, private readonly tokenService: TokenService, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); const langSubscription = this.documentLangSetter.start(); - this.destoryRef.onDestroy(() => langSubscription.unsubscribe()); + this.destroy.onDestroy(() => langSubscription.unsubscribe()); } ngOnInit() { @@ -347,9 +347,9 @@ export class AppComponent implements OnDestroy, OnInit { private idleStateChanged() { if (this.isIdle) { - this.notificationsService.disconnectFromInactivity(); + this.serverNotificationsService.disconnectFromInactivity(); } else { - this.notificationsService.reconnectFromActivity(); + this.serverNotificationsService.reconnectFromActivity(); } } } diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html index 6e87d66d18b..1c04c03a8d2 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access-add-edit.component.html @@ -62,7 +62,7 @@ buttonType="danger" [bitAction]="delete" *ngIf="editMode" - appA11yTitle="{{ 'delete' | i18n }}" + label="{{ 'delete' | i18n }}" > diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html index 8a802e4f6af..70165a94fc3 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.html @@ -89,7 +89,7 @@ @@ -212,7 +212,7 @@ diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html index 1df1e52f7e4..dbad422a32e 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html @@ -39,7 +39,7 @@ type="button" buttonType="danger" (click)="remove(i)" - appA11yTitle="{{ 'remove' | i18n }}" + label="{{ 'remove' | i18n }}" >
diff --git a/apps/web/src/app/billing/members/free-bitwarden-families.component.html b/apps/web/src/app/billing/members/free-bitwarden-families.component.html index ddf7c506745..697a5963a71 100644 --- a/apps/web/src/app/billing/members/free-bitwarden-families.component.html +++ b/apps/web/src/app/billing/members/free-bitwarden-families.component.html @@ -53,7 +53,7 @@ bitIconButton="bwi-ellipsis-v" buttonType="main" [bitMenuTriggerFor]="appListDropdown" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > @if (!isSelfHosted && !sponsoredFamily.validUntil) { diff --git a/apps/web/src/app/billing/organizations/billing-sync-api-key.component.html b/apps/web/src/app/billing/organizations/billing-sync-api-key.component.html index a09000ef55f..465a50ec8c3 100644 --- a/apps/web/src/app/billing/organizations/billing-sync-api-key.component.html +++ b/apps/web/src/app/billing/organizations/billing-sync-api-key.component.html @@ -33,7 +33,7 @@ showToast [valueLabel]="'billingSyncKey' | i18n" [appCopyClick]="clientSecret" - [appA11yTitle]="'copyValue' | i18n" + [label]="'copyValue' | i18n" >
diff --git a/apps/web/src/app/billing/organizations/billing-sync-key.component.html b/apps/web/src/app/billing/organizations/billing-sync-key.component.html index 9736351deca..94a81140344 100644 --- a/apps/web/src/app/billing/organizations/billing-sync-key.component.html +++ b/apps/web/src/app/billing/organizations/billing-sync-key.component.html @@ -33,7 +33,7 @@ bitIconButton="bwi-trash" bitFormButton [bitAction]="deleteConnection" - appA11yTitle="{{ 'delete' | i18n }}" + label="{{ 'delete' | i18n }}" > diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index ace3d749a3f..f899b8eccb4 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -359,7 +359,7 @@ type="button" [bitIconButton]="totalOpened ? 'bwi-angle-down' : 'bwi-angle-up'" size="small" - aria-hidden="true" + [label]="totalOpened ? ('hidePricingSummary' | i18n) : ('showPricingSummary' | i18n)" >

diff --git a/apps/web/src/app/billing/organizations/change-plan.component.html b/apps/web/src/app/billing/organizations/change-plan.component.html index 75a12122d19..1cd15a7c836 100644 --- a/apps/web/src/app/billing/organizations/change-plan.component.html +++ b/apps/web/src/app/billing/organizations/change-plan.component.html @@ -8,7 +8,7 @@ type="button" size="small" class="tw-float-right" - appA11yTitle="{{ 'cancel' | i18n }}" + label="{{ 'cancel' | i18n }}" (click)="cancel()" >

{{ "changeBillingPlan" | i18n }}

diff --git a/apps/web/src/app/billing/settings/sponsoring-org-row.component.html b/apps/web/src/app/billing/settings/sponsoring-org-row.component.html index 1e5690cd85a..5167c0a5c32 100644 --- a/apps/web/src/app/billing/settings/sponsoring-org-row.component.html +++ b/apps/web/src/app/billing/settings/sponsoring-org-row.component.html @@ -12,7 +12,7 @@ bitIconButton="bwi-ellipsis-v" buttonType="main" [bitMenuTriggerFor]="appListDropdown" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" >

diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index a222b668043..965a9d5c99d 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -84,7 +84,7 @@ import { IpcService } from "@bitwarden/common/platform/ipc"; import { UnsupportedWebPushConnectionService, WebPushConnectionService, -} from "@bitwarden/common/platform/notifications/internal"; +} from "@bitwarden/common/platform/server-notifications/internal"; import { AppIdService as DefaultAppIdService } from "@bitwarden/common/platform/services/app-id.service"; import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; diff --git a/apps/web/src/app/core/event.service.ts b/apps/web/src/app/core/event.service.ts index 36d591cc390..ab4d21dab54 100644 --- a/apps/web/src/app/core/event.service.ts +++ b/apps/web/src/app/core/event.service.ts @@ -467,21 +467,60 @@ export class EventService { break; // Secrets Manager case EventType.Secret_Retrieved: - msg = this.i18nService.t("accessedSecretWithId", this.formatSecretId(ev)); + msg = this.i18nService.t("accessedSecretWithId", this.formatSecretId(ev, options)); humanReadableMsg = this.i18nService.t("accessedSecretWithId", this.getShortId(ev.secretId)); break; case EventType.Secret_Created: - msg = this.i18nService.t("createdSecretWithId", this.formatSecretId(ev)); + msg = this.i18nService.t("createdSecretWithId", this.formatSecretId(ev, options)); humanReadableMsg = this.i18nService.t("createdSecretWithId", this.getShortId(ev.secretId)); break; case EventType.Secret_Deleted: - msg = this.i18nService.t("deletedSecretWithId", this.formatSecretId(ev)); + msg = this.i18nService.t("deletedSecretWithId", this.formatSecretId(ev, options)); humanReadableMsg = this.i18nService.t("deletedSecretWithId", this.getShortId(ev.secretId)); break; + case EventType.Secret_Permanently_Deleted: + msg = this.i18nService.t( + "permanentlyDeletedSecretWithId", + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "permanentlyDeletedSecretWithId", + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_Restored: + msg = this.i18nService.t("restoredSecretWithId", this.formatSecretId(ev, options)); + humanReadableMsg = this.i18nService.t("restoredSecretWithId", this.getShortId(ev.secretId)); + break; case EventType.Secret_Edited: - msg = this.i18nService.t("editedSecretWithId", this.formatSecretId(ev)); + msg = this.i18nService.t("editedSecretWithId", this.formatSecretId(ev, options)); humanReadableMsg = this.i18nService.t("editedSecretWithId", this.getShortId(ev.secretId)); break; + case EventType.Project_Retrieved: + msg = this.i18nService.t("accessedProjectWithId", this.formatProjectId(ev, options)); + humanReadableMsg = this.i18nService.t( + "accessedProjectWithId", + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_Created: + msg = this.i18nService.t("createdProjectWithId", this.formatProjectId(ev, options)); + humanReadableMsg = this.i18nService.t( + "createdProjectWithId", + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_Deleted: + msg = this.i18nService.t("deletedProjectWithId", this.formatProjectId(ev, options)); + humanReadableMsg = this.i18nService.t( + "deletedProjectWithId", + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_Edited: + msg = this.i18nService.t("editedProjectWithId", this.formatProjectId(ev, options)); + humanReadableMsg = this.i18nService.t("editedProjectWithId", this.getShortId(ev.projectId)); + break; default: break; } @@ -637,10 +676,41 @@ export class EventService { return a.outerHTML; } - formatSecretId(ev: EventResponse): string { + formatSecretId(ev: EventResponse, options: EventOptions): string { const shortId = this.getShortId(ev.secretId); + if (options.disableLink) { + return shortId; + } const a = this.makeAnchor(shortId); - a.setAttribute("href", "#/sm/" + ev.organizationId + "/secrets?search=" + shortId); + a.setAttribute( + "href", + "#/sm/" + + ev.organizationId + + "/secrets?search=" + + shortId + + "&viewEvents=" + + ev.secretId + + "&type=all", + ); + return a.outerHTML; + } + + formatProjectId(ev: EventResponse, options: EventOptions): string { + const shortId = this.getShortId(ev.projectId); + if (options.disableLink) { + return shortId; + } + const a = this.makeAnchor(shortId); + a.setAttribute( + "href", + "#/sm/" + + ev.organizationId + + "/projects?search=" + + shortId + + "&viewEvents=" + + ev.projectId + + "&type=all", + ); return a.outerHTML; } @@ -684,4 +754,5 @@ export class EventInfo { export class EventOptions { cipherInfo = true; + disableLink = false; } diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index 57d9918aad7..a3358ff7253 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -12,7 +12,7 @@ import { DefaultVaultTimeoutService } from "@bitwarden/common/key-management/vau import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { IpcService } from "@bitwarden/common/platform/ipc"; -import { NotificationsService } from "@bitwarden/common/platform/notifications"; +import { ServerNotificationsService } from "@bitwarden/common/platform/server-notifications"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; @@ -26,7 +26,7 @@ import { VersionService } from "../platform/version.service"; export class InitService { constructor( @Inject(WINDOW) private win: Window, - private notificationsService: NotificationsService, + private serverNotificationsService: ServerNotificationsService, private vaultTimeoutService: DefaultVaultTimeoutService, private i18nService: I18nServiceAbstraction, private eventUploadService: EventUploadServiceAbstraction, @@ -56,7 +56,7 @@ export class InitService { await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(activeAccount.id); } - this.notificationsService.startListening(); + this.serverNotificationsService.startListening(); await this.vaultTimeoutService.init(true); await this.i18nService.init(); (this.eventUploadService as EventUploadService).init(true); diff --git a/apps/web/src/app/core/web-platform-utils.service.ts b/apps/web/src/app/core/web-platform-utils.service.ts index c3d1f5e3c1a..ada0b09aa17 100644 --- a/apps/web/src/app/core/web-platform-utils.service.ts +++ b/apps/web/src/app/core/web-platform-utils.service.ts @@ -98,7 +98,7 @@ export class WebPlatformUtilsService implements PlatformUtilsService { return false; } - isViewOpen(): Promise { + isPopupOpen(): Promise { return Promise.resolve(false); } diff --git a/apps/web/src/app/dirt/reports/reports-layout.component.html b/apps/web/src/app/dirt/reports/reports-layout.component.html index 283d9213cc7..a27556a7aa9 100644 --- a/apps/web/src/app/dirt/reports/reports-layout.component.html +++ b/apps/web/src/app/dirt/reports/reports-layout.component.html @@ -2,7 +2,7 @@
diff --git a/apps/web/src/app/layouts/header/web-header.stories.ts b/apps/web/src/app/layouts/header/web-header.stories.ts index 9715dbf8cd3..7abddf01f2b 100644 --- a/apps/web/src/app/layouts/header/web-header.stories.ts +++ b/apps/web/src/app/layouts/header/web-header.stories.ts @@ -48,7 +48,7 @@ class MockStateService { @Component({ selector: "product-switcher", - template: ``, + template: ``, standalone: false, }) class MockProductSwitcher {} diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html index 204737eee2e..aa853796971 100644 --- a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html +++ b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html @@ -31,7 +31,7 @@
{{ "moreFromBitwarden" | i18n }} diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher.component.html b/apps/web/src/app/layouts/product-switcher/product-switcher.component.html index f1942a02c20..a44f05a7ed7 100644 --- a/apps/web/src/app/layouts/product-switcher/product-switcher.component.html +++ b/apps/web/src/app/layouts/product-switcher/product-switcher.component.html @@ -3,7 +3,7 @@ bitIconButton="bwi bwi-fw bwi-filter" [bitMenuTriggerFor]="content?.menu" [buttonType]="buttonType" - [attr.aria-label]="'switchProducts' | i18n" + [label]="'switchProducts' | i18n" *ngIf="products$ | async" > diff --git a/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts b/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts index 44866285251..c0bc9ed3e77 100644 --- a/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts +++ b/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts @@ -5,7 +5,7 @@ import { SupportStatus } from "@bitwarden/common/platform/misc/support-status"; import { WebPushConnector, WorkerWebPushConnectionService, -} from "@bitwarden/common/platform/notifications/internal"; +} from "@bitwarden/common/platform/server-notifications/internal"; import { UserId } from "@bitwarden/common/types/guid"; export class PermissionsWebPushConnectionService extends WorkerWebPushConnectionService { diff --git a/apps/web/src/app/settings/domain-rules.component.html b/apps/web/src/app/settings/domain-rules.component.html index 8ebeecb429f..880e2a6da0f 100644 --- a/apps/web/src/app/settings/domain-rules.component.html +++ b/apps/web/src/app/settings/domain-rules.component.html @@ -32,7 +32,7 @@ type="button" buttonType="danger" (click)="remove(i)" - appA11yTitle="{{ 'remove' | i18n }}" + label="{{ 'remove' | i18n }}" >
diff --git a/apps/web/src/app/tools/send/send.component.html b/apps/web/src/app/tools/send/send.component.html index 042046b85ff..b79f50311ed 100644 --- a/apps/web/src/app/tools/send/send.component.html +++ b/apps/web/src/app/tools/send/send.component.html @@ -156,7 +156,7 @@ type="button" [bitMenuTriggerFor]="sendOptions" bitIconButton="bwi-ellipsis-v" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > @@ -101,7 +101,7 @@ bitIconButton="bwi-ellipsis-v" type="button" appStopProp - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > diff --git a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html index ad2886b1e59..7cd5129d3f0 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html @@ -65,7 +65,7 @@ size="small" bitIconButton="bwi-ellipsis-v" type="button" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" appStopProp > } 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 ef928903a72..5ddccf6a395 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 @@ -56,7 +56,7 @@ bitIconButton="bwi-ellipsis-v" size="small" type="button" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > @@ -67,8 +67,7 @@ bitIconButton="bwi-trash" buttonType="danger" size="default" - title="{{ 'delete' | i18n }}" - aria-label="Delete" + label="{{ 'delete' | i18n }}" [bitAction]="deleteDomain" type="submit" bitFormButton diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html index 7ade2e6c63d..38cb077c623 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.html @@ -29,7 +29,7 @@ bitSuffix bitIconButton="bwi-clone" [bitAction]="copyScimUrl" - [appA11yTitle]="'copyScimUrl' | i18n" + [label]="'copyScimUrl' | i18n" > @@ -46,7 +46,7 @@ bitSuffix [bitIconButton]="showScimKey ? 'bwi-eye-slash' : 'bwi-eye'" [bitAction]="toggleScimKey" - [appA11yTitle]="'toggleVisibility' | i18n" + [label]="'toggleVisibility' | i18n" > {{ "scimApiKeyHelperText" | i18n }} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.html index 60993f5570c..08e694aa45a 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/add-edit-member-dialog.component.html @@ -58,7 +58,7 @@ bitIconButton="bwi-trash" buttonType="danger" bitFormButton - [appA11yTitle]="'delete' | i18n" + [label]="'delete' | i18n" [bitAction]="delete" [disabled]="loading" > diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts index 2ad2ecdccbd..d8ad2e01343 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts @@ -5,7 +5,9 @@ import { ActivatedRoute, Router } from "@angular/router"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EventResponse } from "@bitwarden/common/models/response/event.response"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -41,6 +43,8 @@ export class EventsComponent extends BaseEventsComponent implements OnInit { private userNamePipe: UserNamePipe, fileDownloadService: FileDownloadService, toastService: ToastService, + accountService: AccountService, + organizationService: OrganizationService, ) { super( eventService, @@ -50,6 +54,9 @@ export class EventsComponent extends BaseEventsComponent implements OnInit { logService, fileDownloadService, toastService, + route, + accountService, + organizationService, ); } @@ -69,6 +76,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit { } async load() { + this.initBase(); const response = await this.apiService.getProviderUsers(this.providerId); response.data.forEach((u) => { const name = this.userNamePipe.transform(u); 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 f203b7a934a..07ccd997b96 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 @@ -79,7 +79,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts index 53b54e459ea..edad6616fb3 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts @@ -3,7 +3,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { firstValueFrom, Subject, switchMap } from "rxjs"; +import { Subject, switchMap } from "rxjs"; import { first, takeUntil } from "rxjs/operators"; import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components"; @@ -11,8 +11,6 @@ import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/a import { ProviderSetupRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-setup.request"; import { PaymentMethodType } from "@bitwarden/common/billing/enums"; import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { ProviderKey } from "@bitwarden/common/types/key"; @@ -41,10 +39,6 @@ export class SetupComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); - requireProviderPaymentMethodDuringSetup$ = this.configService.getFeatureFlag$( - FeatureFlag.PM19956_RequireProviderPaymentMethodDuringSetup, - ); - constructor( private router: Router, private i18nService: I18nService, @@ -52,7 +46,6 @@ export class SetupComponent implements OnInit, OnDestroy { private keyService: KeyService, private syncService: SyncService, private validationService: ValidationService, - private configService: ConfigService, private providerApiService: ProviderApiServiceAbstraction, private formBuilder: FormBuilder, private toastService: ToastService, @@ -117,15 +110,9 @@ export class SetupComponent implements OnInit, OnDestroy { submit = async () => { try { - const requireProviderPaymentMethodDuringSetup = await firstValueFrom( - this.requireProviderPaymentMethodDuringSetup$, - ); - this.formGroup.markAllAsTouched(); - const paymentValid = requireProviderPaymentMethodDuringSetup - ? this.paymentComponent.validate() - : true; + const paymentValid = this.paymentComponent.validate(); const taxInformationValid = this.taxInformationComponent.validate(); if (!paymentValid || !taxInformationValid || !this.formGroup.valid) { @@ -152,9 +139,7 @@ export class SetupComponent implements OnInit, OnDestroy { request.taxInfo.city = taxInformation.city; request.taxInfo.state = taxInformation.state; - if (requireProviderPaymentMethodDuringSetup) { - request.paymentSource = await this.paymentComponent.tokenize(); - } + request.paymentSource = await this.paymentComponent.tokenize(); const provider = await this.providerApiService.postProviderSetup(this.providerId, request); diff --git a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html index 0be7e904af3..ef8241b534c 100644 --- a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html +++ b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html @@ -181,7 +181,7 @@ bitSuffix type="button" [appCopyClick]="callbackPath" - [appA11yTitle]="'copyValue' | i18n" + [label]="'copyValue' | i18n" > @@ -193,7 +193,7 @@ bitSuffix type="button" [appCopyClick]="signedOutCallbackPath" - [appA11yTitle]="'copyValue' | i18n" + [label]="'copyValue' | i18n" > @@ -336,7 +336,7 @@ bitSuffix type="button" [appCopyClick]="spEntityId" - [appA11yTitle]="'copyValue' | i18n" + [label]="'copyValue' | i18n" > @@ -348,7 +348,7 @@ bitSuffix type="button" [appCopyClick]="spEntityIdStatic" - [appA11yTitle]="'copyValue' | i18n" + [label]="'copyValue' | i18n" > @@ -360,14 +360,14 @@ bitSuffix type="button" [appLaunchClick]="spMetadataUrl" - [appA11yTitle]="'launch' | i18n" + [label]="'launch' | i18n" > @@ -379,7 +379,7 @@ bitSuffix type="button" [appCopyClick]="spAcsUrl" - [appA11yTitle]="'copyValue' | i18n" + [label]="'copyValue' | i18n" > diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html index 043ce65b961..2ab82bd837b 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html @@ -86,7 +86,7 @@ type="button" bitIconButton="bwi-ellipsis-v" size="small" - appA11yTitle="{{ 'options' | i18n }}" + label="{{ 'options' | i18n }}" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.html index ab7c67c7b22..e7f9692beb3 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.html @@ -7,6 +7,7 @@ (click)="toggle()" [attr.aria-expanded]="open" [attr.aria-controls]="contentId" + [label]="'toggleVisibility' | i18n" >
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts index ea5294624af..81a568f0c65 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { combineLatest, firstValueFrom, @@ -17,9 +17,12 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { DialogService } from "@bitwarden/components"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { DialogRef, DialogService, ToastService } from "@bitwarden/components"; +import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component"; import { ProjectListView } from "../../models/view/project-list.view"; +import { ProjectView } from "../../models/view/project.view"; import { BulkConfirmationDetails, BulkConfirmationDialogComponent, @@ -55,6 +58,9 @@ export class ProjectsComponent implements OnInit { private dialogService: DialogService, private organizationService: OrganizationService, private accountService: AccountService, + private toastService: ToastService, + private i18nService: I18nService, + private router: Router, ) {} ngOnInit() { @@ -73,9 +79,53 @@ export class ProjectsComponent implements OnInit { ) )?.enabled; - return await this.getProjects(); + const projects = await this.getProjects(); + const viewEvents = this.route.snapshot.queryParams.viewEvents; + + if (viewEvents) { + const targetProject = projects.find((project) => project.id === viewEvents); + + const userIsAdmin = ( + await firstValueFrom( + this.organizationService + .organizations$(userId) + .pipe(getOrganizationById(params.organizationId)), + ) + )?.isAdmin; + + // They would fall into here if they don't have access to a project, or if it has been permanently deleted. + if (!targetProject) { + //If they are an admin it was permanently deleted and we can show the events with project name redacted + if (userIsAdmin) { + this.openEventsDialogFromEntityId( + this.i18nService.t("nameUnavailableProjectDeleted", viewEvents), + params.organizationId, + viewEvents, + ); + } else { + //They aren't an admin so we don't know if they have access to it, lets show the unknown cipher toast. + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("unknownProject"), + }); + } + } else { + this.openEventsDialog(targetProject); + } + + await this.router.navigate([], { + queryParams: { search: this.search }, + }); + } + + return projects; }), ); + + if (this.route.snapshot.queryParams.search) { + this.search = this.route.snapshot.queryParams.search; + } } private async getProjects(): Promise { @@ -103,6 +153,30 @@ export class ProjectsComponent implements OnInit { }); } + openEventsDialog = (project: ProjectView): DialogRef => + openEntityEventsDialog(this.dialogService, { + data: { + name: project.name, + organizationId: project.organizationId, + entityId: project.id, + entity: "project", + }, + }); + + openEventsDialogFromEntityId = ( + headerName: string, + organizationId: string, + entityId: string, + ): DialogRef => + openEntityEventsDialog(this.dialogService, { + data: { + name: headerName, + organizationId: organizationId, + entityId: entityId, + entity: "project", + }, + }); + async openDeleteProjectDialog(projects: ProjectListView[]) { let projectsToDelete = projects; const readOnlyProjects = projects.filter((project) => project.write == false); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html index 24168d0b025..3a2c858ac31 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html @@ -95,7 +95,7 @@ buttonType="danger" bitIconButton="bwi-trash" bitFormButton - [appA11yTitle]="'delete' | i18n" + [label]="'delete' | i18n" [bitAction]="delete" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts index 18ecd2e3b51..ca093f449c9 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { combineLatestWith, firstValueFrom, Observable, startWith, switchMap } from "rxjs"; import { @@ -13,7 +13,8 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogRef, DialogService, ToastService } from "@bitwarden/components"; +import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component"; import { SecretListView } from "../models/view/secret-list.view"; import { SecretsListComponent } from "../shared/secrets-list.component"; @@ -54,6 +55,8 @@ export class SecretsComponent implements OnInit { private organizationService: OrganizationService, private accountService: AccountService, private logService: LogService, + private toastService: ToastService, + private router: Router, ) {} ngOnInit() { @@ -71,7 +74,53 @@ export class SecretsComponent implements OnInit { ) )?.enabled; - return await this.getSecrets(); + const secrets = await this.getSecrets(); + const viewEvents = this.route.snapshot.queryParams.viewEvents; + + if (viewEvents) { + let targetSecret = secrets.find((secret) => secret.id === viewEvents); + + const userIsAdmin = ( + await firstValueFrom( + this.organizationService + .organizations$(userId) + .pipe(getOrganizationById(params.organizationId)), + ) + )?.isAdmin; + + // Secret might be deleted, make sure they are an admin before checking the trashed secrets + if (!targetSecret && userIsAdmin) { + targetSecret = (await this.secretService.getTrashedSecrets(this.organizationId)).find( + (e) => e.id == viewEvents, + ); + } + + // They would fall into here if they don't have access to a secret, or if it has been permanently deleted. + if (!targetSecret) { + //If they are an admin it was permanently deleted and we can show the events even though we don't have the secret name + if (userIsAdmin) { + this.openEventsDialogByEntityId( + this.i18nService.t("nameUnavailableSecretDeleted", viewEvents), + viewEvents, + ); + } else { + //They aren't an admin so we don't know if they have access to it, lets show the unknown secret toast. + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("unknownSecret"), + }); + } + } else { + this.openEventsDialog(targetSecret); + } + + await this.router.navigate([], { + queryParams: { search: this.search }, + }); + } + + return secrets; }), ); @@ -80,6 +129,26 @@ export class SecretsComponent implements OnInit { } } + openEventsDialogByEntityId = (secretName: string, secretId: string): DialogRef => + openEntityEventsDialog(this.dialogService, { + data: { + name: secretName, + organizationId: this.organizationId, + entityId: secretId, + entity: "secret", + }, + }); + + openEventsDialog = (secret: SecretListView): DialogRef => + openEntityEventsDialog(this.dialogService, { + data: { + name: secret.name, + organizationId: this.organizationId, + entityId: secret.id, + entity: "secret", + }, + }); + private async getSecrets(): Promise { return await this.secretService.getSecrets(this.organizationId); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html index fbb0dd8888a..3399b550ba5 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html @@ -40,8 +40,7 @@ bitIconButton="bwi-ellipsis-v" buttonType="main" [bitMenuTriggerFor]="tableMenu" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" > @@ -65,8 +64,7 @@ type="button" bitIconButton="bwi-ellipsis-v" buttonType="main" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" [bitMenuTriggerFor]="tokenMenu" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.html index b17e47a39ec..11f8e0b9b77 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.html @@ -11,12 +11,19 @@ type="button" bitIconButton="bwi-clone" [bitAction]="copyIdentityUrl" + [label]="'copyCustomField' | i18n: identityUrl" > {{ "apiUrl" | i18n }} - +
@@ -27,6 +34,7 @@ type="button" bitIconButton="bwi-clone" [bitAction]="copyOrganizationId" + [label]="'copyCustomField' | i18n: organizationId" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts index ddaa0937e6f..2e364df1423 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts @@ -2,8 +2,10 @@ // @ts-strict-ignore import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { Subject, takeUntil } from "rxjs"; +import { takeUntil } from "rxjs"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -25,7 +27,6 @@ export class ServiceAccountEventsComponent implements OnInit, OnDestroy { exportFileName = "machine-account-events"; - private destroy$ = new Subject(); private serviceAccountId: string; constructor( @@ -38,6 +39,8 @@ export class ServiceAccountEventsComponent logService: LogService, fileDownloadService: FileDownloadService, toastService: ToastService, + protected organizationService: OrganizationService, + protected accountService: AccountService, ) { super( eventService, @@ -47,10 +50,14 @@ export class ServiceAccountEventsComponent logService, fileDownloadService, toastService, + route, + accountService, + organizationService, ); } async ngOnInit() { + this.initBase(); // eslint-disable-next-line rxjs/no-async-subscribe this.route.params.pipe(takeUntil(this.destroy$)).subscribe(async (params) => { this.serviceAccountId = params.serviceAccountId; @@ -78,9 +85,4 @@ export class ServiceAccountEventsComponent email: "", }; } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html index bfb7b985423..3d7fc9715c3 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html @@ -39,8 +39,7 @@ type="button" bitIconButton="bwi-ellipsis-v" buttonType="main" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" [bitMenuTriggerFor]="tableMenu" > @@ -72,8 +71,7 @@ type="button" bitIconButton="bwi-ellipsis-v" buttonType="main" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" [bitMenuTriggerFor]="serviceAccountMenu" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts index 47d5dd63806..ac3defaf5dd 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts @@ -5,7 +5,6 @@ import { Component, EventEmitter, Input, OnDestroy, Output } from "@angular/core import { Subject, takeUntil } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { TableDataSource, ToastService } from "@bitwarden/components"; import { @@ -49,7 +48,6 @@ export class ServiceAccountsListComponent implements OnDestroy { constructor( private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, private toastService: ToastService, ) { this.selection.changed diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html index c8a50175781..d01faae4e6e 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html @@ -67,8 +67,7 @@ buttonType="main" size="default" [disabled]="disabled" - [attr.title]="'remove' | i18n" - [attr.aria-label]="'remove' | i18n" + [label]="'remove' | i18n" (click)="selectionList.deselectItem(item.id); handleBlur()" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.html index bc1655d8b28..9e31ff628fb 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.html @@ -43,8 +43,7 @@ bitIconButton="bwi-ellipsis-v" buttonType="main" [bitMenuTriggerFor]="tableMenu" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" *ngIf="showMenus" > @@ -77,8 +76,7 @@ bitIconButton="bwi-clone" buttonType="main" size="small" - [title]="'copyUuid' | i18n" - [attr.aria-label]="'copyUuid' | i18n" + [label]="'copyUuid' | i18n" (click)="copyProjectUuidToClipboard(project.id)" > @@ -94,8 +92,7 @@ bitIconButton="bwi-ellipsis-v" buttonType="main" [bitMenuTriggerFor]="projectMenu" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" *ngIf="showMenus" > @@ -117,6 +114,15 @@ {{ "deleteProject" | i18n }} +
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts index 9774172cd4b..31114bcd1c4 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts @@ -1,21 +1,31 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { SelectionModel } from "@angular/cdk/collections"; -import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { map } from "rxjs"; +import { Component, EventEmitter, Input, Output, OnInit } from "@angular/core"; +import { ActivatedRoute } from "@angular/router"; +import { catchError, concatMap, map, Observable, of, Subject, switchMap, takeUntil } from "rxjs"; +import { + getOrganizationById, + OrganizationService, +} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { TableDataSource, ToastService } from "@bitwarden/components"; +import { DialogRef, DialogService, TableDataSource, ToastService } from "@bitwarden/components"; +import { LogService } from "@bitwarden/logging"; +import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component"; import { ProjectListView } from "../models/view/project-list.view"; +import { ProjectView } from "../models/view/project.view"; @Component({ selector: "sm-projects-list", templateUrl: "./projects-list.component.html", standalone: false, }) -export class ProjectsListComponent { +export class ProjectsListComponent implements OnInit { @Input() get projects(): ProjectListView[] { return this._projects; @@ -26,6 +36,9 @@ export class ProjectsListComponent { this.dataSource.data = projects; } private _projects: ProjectListView[]; + protected viewEventsAllowed$: Observable; + protected isAdmin$: Observable; + private destroy$: Subject = new Subject(); @Input() showMenus?: boolean = true; @@ -50,8 +63,41 @@ export class ProjectsListComponent { private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private toastService: ToastService, + private dialogService: DialogService, + private organizationService: OrganizationService, + private activatedRoute: ActivatedRoute, + private accountService: AccountService, + private logService: LogService, ) {} + ngOnInit(): void { + this.viewEventsAllowed$ = this.activatedRoute.params.pipe( + concatMap((params) => + getUserId(this.accountService.activeAccount$).pipe( + switchMap((userId) => + this.organizationService + .organizations$(userId) + .pipe(getOrganizationById(params.organizationId)), + ), + ), + ), + map((org) => org.canAccessEventLogs), + catchError((error: unknown) => { + if (typeof error === "string") { + this.toastService.showToast({ + message: error, + variant: "error", + title: "", + }); + } else { + this.logService.error(error); + } + return of(false); + }), + takeUntil(this.destroy$), + ); + } + isAllSelected() { if (this.selection.selected?.length > 0) { const numSelected = this.selection.selected.length; @@ -87,6 +133,16 @@ export class ProjectsListComponent { } } + openEventsDialog = (project: ProjectView): DialogRef => + openEntityEventsDialog(this.dialogService, { + data: { + name: project.name, + organizationId: project.organizationId, + entityId: project.id, + entity: "project", + }, + }); + private selectedHasWriteAccess() { const selectedProjects = this.projects.filter((project) => this.selection.isSelected(project.id), diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html index 859c7417eb8..25f2447246a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html @@ -45,8 +45,7 @@ type="button" bitIconButton="bwi-ellipsis-v" buttonType="main" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" [bitMenuTriggerFor]="tableMenu" > @@ -78,8 +77,7 @@ bitIconButton="bwi-clone" buttonType="main" size="small" - [title]="'copyUuid' | i18n" - [attr.aria-label]="'copyUuid' | i18n" + [label]="'copyUuid' | i18n" (click)="copySecretUuidEvent.emit(secret.id)" > @@ -108,8 +106,7 @@ type="button" bitIconButton="bwi-ellipsis-v" buttonType="main" - [title]="'options' | i18n" - [attr.aria-label]="'options' | i18n" + [label]="'options' | i18n" [bitMenuTriggerFor]="secretMenu" > @@ -151,6 +148,15 @@ {{ "restoreSecret" | i18n }} + diff --git a/libs/auth/src/angular/input-password/input-password.component.html b/libs/auth/src/angular/input-password/input-password.component.html index d39215b2d68..d56fe6a27fc 100644 --- a/libs/auth/src/angular/input-password/input-password.component.html +++ b/libs/auth/src/angular/input-password/input-password.component.html @@ -45,7 +45,7 @@ type="button" bitIconButton="bwi-generate" bitSuffix - [appA11yTitle]="'generatePassword' | i18n" + [label]="'generatePassword' | i18n" (click)="generatePassword()" >
- + ``` ## `[bitSubmit]` Disabled Form Submit diff --git a/libs/components/src/async-actions/in-forms.stories.ts b/libs/components/src/async-actions/in-forms.stories.ts index dd901cd2477..88383fe85a3 100644 --- a/libs/components/src/async-actions/in-forms.stories.ts +++ b/libs/components/src/async-actions/in-forms.stories.ts @@ -7,6 +7,7 @@ import { delay, of } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { A11yTitleDirective } from "../a11y"; import { ButtonModule } from "../button"; import { FormFieldModule } from "../form-field"; import { IconButtonModule } from "../icon-button"; @@ -28,20 +29,21 @@ const template = ` Email - + - + `; @Component({ selector: "app-promise-example", template, imports: [ + A11yTitleDirective, AsyncActionsModule, ButtonModule, FormFieldModule, @@ -86,6 +88,7 @@ class PromiseExampleComponent { selector: "app-observable-example", template, imports: [ + A11yTitleDirective, AsyncActionsModule, ButtonModule, FormFieldModule, diff --git a/libs/components/src/async-actions/standalone.mdx b/libs/components/src/async-actions/standalone.mdx index f484ea01c58..a781f40d852 100644 --- a/libs/components/src/async-actions/standalone.mdx +++ b/libs/components/src/async-actions/standalone.mdx @@ -63,7 +63,7 @@ from how click handlers are usually defined with the output syntax `(click)="han ```html -`; +`; ``` ## Stories diff --git a/libs/components/src/async-actions/standalone.stories.ts b/libs/components/src/async-actions/standalone.stories.ts index 1ed6f6c5a59..99cde70566b 100644 --- a/libs/components/src/async-actions/standalone.stories.ts +++ b/libs/components/src/async-actions/standalone.stories.ts @@ -16,7 +16,7 @@ const template = /*html*/ ` - `; + `; @Component({ template, diff --git a/libs/components/src/avatar/avatar.component.ts b/libs/components/src/avatar/avatar.component.ts index 59a9492f8c8..6f83c9ca101 100644 --- a/libs/components/src/avatar/avatar.component.ts +++ b/libs/components/src/avatar/avatar.component.ts @@ -7,11 +7,11 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; type SizeTypes = "xlarge" | "large" | "default" | "small" | "xsmall"; const SizeClasses: Record = { - xlarge: ["tw-h-24", "tw-w-24"], - large: ["tw-h-16", "tw-w-16"], - default: ["tw-h-10", "tw-w-10"], - small: ["tw-h-7", "tw-w-7"], - xsmall: ["tw-h-6", "tw-w-6"], + xlarge: ["tw-h-24", "tw-w-24", "tw-min-w-24"], + large: ["tw-h-16", "tw-w-16", "tw-min-w-16"], + default: ["tw-h-10", "tw-w-10", "tw-min-w-10"], + small: ["tw-h-7", "tw-w-7", "tw-min-w-7"], + xsmall: ["tw-h-6", "tw-w-6", "tw-min-w-6"], }; /** diff --git a/libs/components/src/banner/banner.component.html b/libs/components/src/banner/banner.component.html index 581a56d86cb..bfde8135da9 100644 --- a/libs/components/src/banner/banner.component.html +++ b/libs/components/src/banner/banner.component.html @@ -19,8 +19,7 @@ buttonType="main" size="small" (click)="onClose.emit()" - [attr.title]="'close' | i18n" - [attr.aria-label]="'close' | i18n" + [label]="'close' | i18n" > } diff --git a/libs/components/src/breadcrumbs/breadcrumbs.component.html b/libs/components/src/breadcrumbs/breadcrumbs.component.html index d062e82548e..b63b21de76b 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.component.html +++ b/libs/components/src/breadcrumbs/breadcrumbs.component.html @@ -35,6 +35,7 @@ bitIconButton="bwi-ellipsis-h" [bitMenuTriggerFor]="overflowMenu" size="small" + [label]="'moreBreadcrumbs' | i18n" > @for (breadcrumb of overflow; track breadcrumb) { diff --git a/libs/components/src/breadcrumbs/breadcrumbs.component.ts b/libs/components/src/breadcrumbs/breadcrumbs.component.ts index a1a6e732459..3c24f91be99 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.component.ts +++ b/libs/components/src/breadcrumbs/breadcrumbs.component.ts @@ -2,6 +2,8 @@ import { CommonModule } from "@angular/common"; import { Component, ContentChildren, QueryList, input } from "@angular/core"; import { RouterModule } from "@angular/router"; +import { I18nPipe } from "@bitwarden/ui-common"; + import { IconButtonModule } from "../icon-button"; import { LinkModule } from "../link"; import { MenuModule } from "../menu"; @@ -16,7 +18,7 @@ import { BreadcrumbComponent } from "./breadcrumb.component"; @Component({ selector: "bit-breadcrumbs", templateUrl: "./breadcrumbs.component.html", - imports: [CommonModule, LinkModule, RouterModule, IconButtonModule, MenuModule], + imports: [I18nPipe, CommonModule, LinkModule, RouterModule, IconButtonModule, MenuModule], }) export class BreadcrumbsComponent { readonly show = input(3); diff --git a/libs/components/src/breadcrumbs/breadcrumbs.stories.ts b/libs/components/src/breadcrumbs/breadcrumbs.stories.ts index 98af3c0ae7b..893f645a913 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.stories.ts +++ b/libs/components/src/breadcrumbs/breadcrumbs.stories.ts @@ -2,9 +2,12 @@ import { Component, importProvidersFrom } from "@angular/core"; import { RouterModule } from "@angular/router"; import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + import { IconButtonModule } from "../icon-button"; import { LinkModule } from "../link"; import { MenuModule } from "../menu"; +import { I18nMockService } from "../utils"; import { BreadcrumbComponent } from "./breadcrumb.component"; import { BreadcrumbsComponent } from "./breadcrumbs.component"; @@ -26,6 +29,16 @@ export default { decorators: [ moduleMetadata({ imports: [LinkModule, MenuModule, IconButtonModule, RouterModule, BreadcrumbComponent], + providers: [ + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + moreBreadcrumbs: "More breadcrumbs", + }); + }, + }, + ], }), applicationConfig({ providers: [ diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index 94fc9ef06a0..9f140ca7624 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -37,8 +37,7 @@ buttonType="main" size="default" bitDialogClose - [attr.title]="'close' | i18n" - [attr.aria-label]="'close' | i18n" + [label]="'close' | i18n" > } diff --git a/libs/components/src/dialog/dialog/dialog.stories.ts b/libs/components/src/dialog/dialog/dialog.stories.ts index 71e07542ed8..f93ef1a2f25 100644 --- a/libs/components/src/dialog/dialog/dialog.stories.ts +++ b/libs/components/src/dialog/dialog/dialog.stories.ts @@ -101,8 +101,7 @@ export const Default: Story = { bitIconButton="bwi-trash" buttonType="danger" size="default" - title="Delete" - aria-label="Delete"> + label="Delete"> `, @@ -219,7 +218,7 @@ export const WithCards: Story = {

Foo

- + @@ -239,7 +238,7 @@ export const WithCards: Story = {

Bar

- + @@ -265,8 +264,7 @@ export const WithCards: Story = { bitIconButton="bwi-trash" buttonType="danger" size="default" - title="Delete" - aria-label="Delete"> + label="Delete"> diff --git a/libs/components/src/disclosure/disclosure.component.ts b/libs/components/src/disclosure/disclosure.component.ts index 703c815087b..2d73d7d8ad6 100644 --- a/libs/components/src/disclosure/disclosure.component.ts +++ b/libs/components/src/disclosure/disclosure.component.ts @@ -28,6 +28,7 @@ let nextId = 0; * bitIconButton="bwi-sliders" * [buttonType]="'muted'" * [bitDisclosureTriggerFor]="disclosureRef" + * [label]="'Settings' | i18n" * > * click button to hide this content * ``` diff --git a/libs/components/src/disclosure/disclosure.stories.ts b/libs/components/src/disclosure/disclosure.stories.ts index bb3680c1f3b..2e45964ccaa 100644 --- a/libs/components/src/disclosure/disclosure.stories.ts +++ b/libs/components/src/disclosure/disclosure.stories.ts @@ -27,7 +27,7 @@ export const DisclosureWithIconButton: Story = { render: (args) => ({ props: args, template: /*html*/ ` - click button to hide this content `, diff --git a/libs/components/src/drawer/drawer-header.component.html b/libs/components/src/drawer/drawer-header.component.html index 863b19edfb2..2723744eda3 100644 --- a/libs/components/src/drawer/drawer-header.component.html +++ b/libs/components/src/drawer/drawer-header.component.html @@ -5,11 +5,5 @@ {{ title() }}

- + diff --git a/libs/components/src/form-field/form-field.stories.ts b/libs/components/src/form-field/form-field.stories.ts index e070765ec8a..7aeb2f63040 100644 --- a/libs/components/src/form-field/form-field.stories.ts +++ b/libs/components/src/form-field/form-field.stories.ts @@ -239,8 +239,8 @@ export const Readonly: Story = { Input - - + + @@ -261,7 +261,7 @@ export const Readonly: Story = { Input - + @@ -309,11 +309,11 @@ export const ButtonInputGroup: Story = { - + - - - + + + `, }), @@ -326,11 +326,11 @@ export const DisabledButtonInputGroup: Story = { template: /*html*/ ` Label - + - - - + + + `, @@ -345,9 +345,9 @@ export const PartiallyDisabledButtonInputGroup: Story = { Label - - - + + + `, }), diff --git a/libs/components/src/form-field/password-input-toggle.spec.ts b/libs/components/src/form-field/password-input-toggle.spec.ts index 95110f2bd93..72f2481d789 100644 --- a/libs/components/src/form-field/password-input-toggle.spec.ts +++ b/libs/components/src/form-field/password-input-toggle.spec.ts @@ -20,7 +20,13 @@ import { BitPasswordInputToggleDirective } from "./password-input-toggle.directi Password - + `, diff --git a/libs/components/src/form-field/password-input-toggle.stories.ts b/libs/components/src/form-field/password-input-toggle.stories.ts index d46ec92ab37..3d50a4eb75a 100644 --- a/libs/components/src/form-field/password-input-toggle.stories.ts +++ b/libs/components/src/form-field/password-input-toggle.stories.ts @@ -48,7 +48,7 @@ export const Default: Story = { Password - + `, @@ -63,7 +63,7 @@ export const Binding: Story = { Password - +