1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00

Update at-risk cards with accessibility improvements (#17427)

This commit is contained in:
Maximilian Power
2025-11-17 20:08:15 +01:00
committed by GitHub
parent b296750bcb
commit 4e2d8988f2
4 changed files with 186 additions and 92 deletions

View File

@@ -5,42 +5,85 @@
<div class="tw-mt-4 tw-flex tw-flex-col"> <div class="tw-mt-4 tw-flex tw-flex-col">
<h2 class="tw-mb-6" bitTypography="h2">{{ "allApplications" | i18n }}</h2> <h2 class="tw-mb-6" bitTypography="h2">{{ "allApplications" | i18n }}</h2>
<div class="tw-flex tw-gap-6"> <div class="tw-flex tw-gap-6">
<button <div
type="button" role="region"
class="tw-flex-1" [attr.aria-label]="'atRiskMembers' | i18n"
tabindex="0" class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4"
(click)="dataService.setDrawerForOrgAtRiskMembers('allAppsOrgAtRiskMembers')" [ngClass]="{
'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskMembers',
}"
> >
<dirt-card <div class="tw-flex tw-flex-col tw-gap-1">
#allAppsOrgAtRiskMembers <span bitTypography="h6" class="tw-flex tw-text-main" id="allAppsOrgAtRiskMembersLabel">{{
class="tw-w-full" "atRiskMembers" | i18n
[ngClass]="{ }}</span>
'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskMembers', <div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite">
}" <span
[title]="'atRiskMembers' | i18n" bitTypography="h3"
[value]="applicationSummary.totalAtRiskMemberCount" class="!tw-mb-0"
[maxValue]="applicationSummary.totalMemberCount" aria-describedby="allAppsOrgAtRiskMembersLabel"
> >{{ applicationSummary.totalAtRiskMemberCount }}</span
</dirt-card> >
</button> <span bitTypography="body2">{{
<button "cardMetrics" | i18n: applicationSummary.totalMemberCount
type="button" }}</span>
class="tw-flex-1" </div>
tabindex="0" <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2">
(click)="dataService.setDrawerForOrgAtRiskApps('allAppsOrgAtRiskApplications')" <p bitTypography="body1" class="tw-mb-0">
<button
type="button"
bitLink
[attr.aria-label]="('viewAtRiskMembers' | i18n) + ': ' + ('atRiskMembers' | i18n)"
(click)="dataService.setDrawerForOrgAtRiskMembers('allAppsOrgAtRiskMembers')"
>
{{ "viewAtRiskMembers" | i18n }}
</button>
</p>
</div>
</div>
</div>
<div
role="region"
[attr.aria-label]="'atRiskApplications' | i18n"
class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4"
[ngClass]="{
'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskApplications',
}"
> >
<dirt-card <div class="tw-flex tw-flex-col tw-gap-1">
#allAppsOrgAtRiskApplications <span
class="tw-w-full" bitTypography="h6"
[ngClass]="{ class="tw-flex tw-text-main"
'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskApplications', id="allAppsOrgAtRiskApplicationsLabel"
}" >{{ "atRiskApplications" | i18n }}</span
[title]="'atRiskApplications' | i18n" >
[value]="applicationSummary.totalAtRiskApplicationCount" <div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite">
[maxValue]="applicationSummary.totalApplicationCount" <span
> bitTypography="h3"
</dirt-card> class="!tw-mb-0"
</button> aria-describedby="allAppsOrgAtRiskApplicationsLabel"
>{{ applicationSummary.totalAtRiskApplicationCount }}</span
>
<span bitTypography="body2">{{
"cardMetrics" | i18n: applicationSummary.totalApplicationCount
}}</span>
</div>
<div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2">
<p bitTypography="body1" class="tw-mb-0">
<button
type="button"
bitLink
[attr.aria-label]="
('viewAtRiskApplications' | i18n) + ': ' + ('atRiskApplications' | i18n)
"
(click)="dataService.setDrawerForOrgAtRiskApps('allAppsOrgAtRiskApplications')"
>
{{ "viewAtRiskApplications" | i18n }}
</button>
</p>
</div>
</div>
</div>
</div> </div>
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4"> <div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
<bit-search <bit-search

View File

@@ -18,12 +18,13 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { import {
IconButtonModule, IconButtonModule,
LinkModule,
NoItemsModule, NoItemsModule,
SearchModule, SearchModule,
TableDataSource, TableDataSource,
ToastService, ToastService,
TypographyModule,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { CardComponent } from "@bitwarden/dirt-card";
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
@@ -39,13 +40,14 @@ import { ApplicationsLoadingComponent } from "../shared/risk-insights-loading.co
imports: [ imports: [
ApplicationsLoadingComponent, ApplicationsLoadingComponent,
HeaderModule, HeaderModule,
CardComponent, LinkModule,
SearchModule, SearchModule,
PipesModule, PipesModule,
NoItemsModule, NoItemsModule,
SharedModule, SharedModule,
AppTableRowScrollableComponent, AppTableRowScrollableComponent,
IconButtonModule, IconButtonModule,
TypographyModule,
], ],
}) })
export class AllApplicationsComponent implements OnInit { export class AllApplicationsComponent implements OnInit {

View File

@@ -1,3 +1,4 @@
@let drawerDetails = dataService.drawerDetails$ | async;
<div class="tw-mt-4 tw-flex tw-flex-col"> <div class="tw-mt-4 tw-flex tw-flex-col">
<div class="tw-flex tw-justify-between tw-mb-4"> <div class="tw-flex tw-justify-between tw-mb-4">
<h2 bitTypography="h2">{{ "criticalApplications" | i18n }}</h2> <h2 bitTypography="h2">{{ "criticalApplications" | i18n }}</h2>
@@ -16,60 +17,101 @@
}} }}
</button> </button>
</div> </div>
@if (dataService.drawerDetails$ | async; as drawerDetails) { <div class="tw-flex tw-gap-6">
<div class="tw-flex tw-gap-6"> <div
<button role="region"
type="button" [attr.aria-label]="'atRiskMembers' | i18n"
class="tw-flex-1" class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4"
tabindex="0" [ngClass]="{
(click)="dataService.setDrawerForCriticalAtRiskMembers('criticalAppsAtRiskMembers')" 'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskMembers',
> }"
<dirt-card >
#criticalAppsAtRiskMembers <div class="tw-flex tw-flex-col tw-gap-1">
class="tw-w-full" <span bitTypography="h6" class="tw-flex tw-text-main" id="criticalAppsAtRiskMembersLabel">{{
[ngClass]="{ "atRiskMembers" | i18n
'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskMembers', }}</span>
}" <div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite">
[title]="'atRiskMembers' | i18n" <span
[value]="applicationSummary.totalAtRiskMemberCount" bitTypography="h3"
[maxValue]="applicationSummary.totalMemberCount" class="!tw-mb-0"
> aria-describedby="criticalAppsAtRiskMembersLabel"
</dirt-card> >{{ applicationSummary.totalAtRiskMemberCount }}</span
</button> >
<button <span bitTypography="body2">{{
type="button" "cardMetrics" | i18n: applicationSummary.totalMemberCount
class="tw-flex-1" }}</span>
tabindex="0" </div>
(click)="dataService.setDrawerForCriticalAtRiskApps('criticalAppsAtRiskApplications')" <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2">
> <p bitTypography="body1" class="tw-mb-0">
<dirt-card <button
#criticalAppsAtRiskApplications type="button"
class="tw-w-full" bitLink
[ngClass]="{ [attr.aria-label]="('viewAtRiskMembers' | i18n) + ': ' + ('atRiskMembers' | i18n)"
'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskApplications', (click)="dataService.setDrawerForCriticalAtRiskMembers('criticalAppsAtRiskMembers')"
}" >
[title]="'atRiskApplications' | i18n" {{ "viewAtRiskMembers" | i18n }}
[value]="applicationSummary.totalAtRiskApplicationCount" </button>
[maxValue]="applicationSummary.totalApplicationCount" </p>
> </div>
</dirt-card> </div>
</button>
</div> </div>
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4"> <div
<bit-search role="region"
[placeholder]="'searchApps' | i18n" [attr.aria-label]="'atRiskApplications' | i18n"
class="tw-grow" class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4"
[formControl]="searchControl" [ngClass]="{
></bit-search> 'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskApplications',
}"
>
<div class="tw-flex tw-flex-col tw-gap-1">
<span
bitTypography="h6"
class="tw-flex tw-text-main"
id="criticalAppsAtRiskApplicationsLabel"
>{{ "atRiskApplications" | i18n }}</span
>
<div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite">
<span
bitTypography="h3"
class="!tw-mb-0"
aria-describedby="criticalAppsAtRiskApplicationsLabel"
>{{ applicationSummary.totalAtRiskApplicationCount }}</span
>
<span bitTypography="body2">{{
"cardMetrics" | i18n: applicationSummary.totalApplicationCount
}}</span>
</div>
<div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2">
<p bitTypography="body1" class="tw-mb-0">
<button
type="button"
bitLink
[attr.aria-label]="
('viewAtRiskApplications' | i18n) + ': ' + ('atRiskApplications' | i18n)
"
(click)="dataService.setDrawerForCriticalAtRiskApps('criticalAppsAtRiskApplications')"
>
{{ "viewAtRiskApplications" | i18n }}
</button>
</p>
</div>
</div>
</div> </div>
</div>
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
<bit-search
[placeholder]="'searchApps' | i18n"
class="tw-grow"
[formControl]="searchControl"
></bit-search>
</div>
<app-table-row-scrollable <app-table-row-scrollable
[dataSource]="dataSource" [dataSource]="dataSource"
[showRowCheckBox]="false" [showRowCheckBox]="false"
[showRowMenuForCriticalApps]="true" [showRowMenuForCriticalApps]="true"
[openApplication]="drawerDetails.invokerId || ''" [openApplication]="drawerDetails.invokerId || ''"
[showAppAtRiskMembers]="showAppAtRiskMembers" [showAppAtRiskMembers]="showAppAtRiskMembers"
[unmarkAsCritical]="removeCriticalApplication" [unmarkAsCritical]="removeCriticalApplication"
></app-table-row-scrollable> ></app-table-row-scrollable>
}
</div> </div>

View File

@@ -17,8 +17,14 @@ import { createNewSummaryData } from "@bitwarden/bit-common/dirt/reports/risk-in
import { OrganizationReportSummary } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models"; import { OrganizationReportSummary } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { OrganizationId } from "@bitwarden/common/types/guid"; import { OrganizationId } from "@bitwarden/common/types/guid";
import { NoItemsModule, SearchModule, TableDataSource, ToastService } from "@bitwarden/components"; import {
import { CardComponent } from "@bitwarden/dirt-card"; LinkModule,
NoItemsModule,
SearchModule,
TableDataSource,
ToastService,
TypographyModule,
} from "@bitwarden/components";
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
@@ -33,13 +39,14 @@ import { AccessIntelligenceSecurityTasksService } from "../shared/security-tasks
selector: "dirt-critical-applications", selector: "dirt-critical-applications",
templateUrl: "./critical-applications.component.html", templateUrl: "./critical-applications.component.html",
imports: [ imports: [
CardComponent,
HeaderModule, HeaderModule,
LinkModule,
SearchModule, SearchModule,
NoItemsModule, NoItemsModule,
PipesModule, PipesModule,
SharedModule, SharedModule,
AppTableRowScrollableComponent, AppTableRowScrollableComponent,
TypographyModule,
], ],
}) })
export class CriticalApplicationsComponent implements OnInit { export class CriticalApplicationsComponent implements OnInit {