mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-27711] Loading Skeleton page (#17224)
* convert popup page component to use inputs * disable overflow on popup page to allow content to naturally overflow * migrate popup-page to OnPush * add vault-loading-skeleton component * remove internal loading text * hide entire skeleton from screen readers
This commit is contained in:
@@ -29,11 +29,9 @@ import {
|
||||
SearchModule,
|
||||
SectionComponent,
|
||||
ScrollLayoutDirective,
|
||||
SkeletonComponent,
|
||||
SkeletonTextComponent,
|
||||
SkeletonGroupComponent,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { VaultLoadingSkeletonComponent } from "../../../vault/popup/components/vault-loading-skeleton/vault-loading-skeleton.component";
|
||||
import { PopupRouterCacheService } from "../view-cache/popup-router-cache.service";
|
||||
|
||||
import { PopupFooterComponent } from "./popup-footer.component";
|
||||
@@ -366,9 +364,7 @@ export default {
|
||||
SectionComponent,
|
||||
IconButtonModule,
|
||||
BadgeModule,
|
||||
SkeletonComponent,
|
||||
SkeletonTextComponent,
|
||||
SkeletonGroupComponent,
|
||||
VaultLoadingSkeletonComponent,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
@@ -634,21 +630,9 @@ export const SkeletonLoading: Story = {
|
||||
template: /* HTML */ `
|
||||
<extension-container>
|
||||
<popup-tab-navigation>
|
||||
<popup-page>
|
||||
<popup-page hideOverflow>
|
||||
<popup-header slot="header" pageTitle="Page Header"></popup-header>
|
||||
<div>
|
||||
<div class="tw-sr-only" role="status">Loading...</div>
|
||||
<div class="tw-flex tw-flex-col tw-gap-4">
|
||||
<bit-skeleton-text class="tw-w-1/3"></bit-skeleton-text>
|
||||
@for (num of data; track $index) {
|
||||
<bit-skeleton-group>
|
||||
<bit-skeleton class="tw-size-8" slot="start"></bit-skeleton>
|
||||
<bit-skeleton-text [lines]="2" class="tw-w-1/2"></bit-skeleton-text>
|
||||
</bit-skeleton-group>
|
||||
<bit-skeleton class="tw-w-full tw-h-[1px]"></bit-skeleton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<vault-loading-skeleton></vault-loading-skeleton>
|
||||
</popup-page>
|
||||
</popup-tab-navigation>
|
||||
</extension-container>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<ng-content select="[slot=header]"></ng-content>
|
||||
<main class="tw-flex-1 tw-overflow-hidden tw-flex tw-flex-col tw-relative tw-bg-background-alt">
|
||||
<ng-content select="[slot=full-width-notice]"></ng-content>
|
||||
<!--
|
||||
<!--
|
||||
x padding on this container is designed to always be a minimum of 0.75rem (equivalent to tailwind's tw-px-3), or 0.5rem (equivalent
|
||||
to tailwind's tw-px-2) in compact mode, but stretch to fill the remainder of the container when the content reaches a maximum of
|
||||
640px in width (equivalent to tailwind's `sm` breakpoint)
|
||||
@@ -10,26 +10,28 @@
|
||||
#nonScrollable
|
||||
class="tw-transition-colors tw-duration-200 tw-border-0 tw-border-b tw-border-solid tw-py-3 bit-compact:tw-py-2 tw-px-[max(0.75rem,calc((100%-(var(--tw-sm-breakpoint)))/2))] bit-compact:tw-px-[max(0.5rem,calc((100%-(var(--tw-sm-breakpoint)))/2))]"
|
||||
[ngClass]="{
|
||||
'tw-invisible !tw-p-0 !tw-border-none': loading || nonScrollable.childElementCount === 0,
|
||||
'tw-invisible !tw-p-0 !tw-border-none': loading() || nonScrollable.childElementCount === 0,
|
||||
'tw-border-secondary-300': scrolled(),
|
||||
'tw-border-transparent': !scrolled(),
|
||||
}"
|
||||
>
|
||||
<ng-content select="[slot=above-scroll-area]"></ng-content>
|
||||
</div>
|
||||
<!--
|
||||
<!--
|
||||
x padding on this container is designed to always be a minimum of 0.75rem (equivalent to tailwind's tw-px-3), or 0.5rem (equivalent
|
||||
to tailwind's tw-px-2) in compact mode, but stretch to fill the remainder of the container when the content reaches a maximum of
|
||||
640px in width (equivalent to tailwind's `sm` breakpoint)
|
||||
-->
|
||||
<div
|
||||
class="tw-overflow-y-auto tw-size-full tw-styled-scrollbar"
|
||||
class="tw-size-full tw-styled-scrollbar"
|
||||
data-testid="popup-layout-scroll-region"
|
||||
(scroll)="handleScroll($event)"
|
||||
[ngClass]="{
|
||||
'tw-invisible': loading,
|
||||
'tw-overflow-hidden': hideOverflow(),
|
||||
'tw-overflow-y-auto': !hideOverflow(),
|
||||
'tw-invisible': loading(),
|
||||
'tw-py-3 bit-compact:tw-py-2 tw-px-[max(0.75rem,calc((100%-(var(--tw-sm-breakpoint)))/2))] bit-compact:tw-px-[max(0.5rem,calc((100%-(var(--tw-sm-breakpoint)))/2))]':
|
||||
!disablePadding,
|
||||
!disablePadding(),
|
||||
}"
|
||||
bitScrollLayoutHost
|
||||
>
|
||||
@@ -37,9 +39,9 @@
|
||||
</div>
|
||||
<span
|
||||
class="tw-absolute tw-inset-0 tw-flex tw-items-center tw-justify-center tw-text-main"
|
||||
[ngClass]="{ 'tw-invisible': !loading }"
|
||||
[ngClass]="{ 'tw-invisible': !loading() }"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-lg bwi-spin" [attr.aria-label]="loadingText"></i>
|
||||
<i class="bwi bwi-spinner bwi-lg bwi-spin" [attr.aria-label]="loadingText()"></i>
|
||||
</span>
|
||||
</main>
|
||||
<ng-content select="[slot=footer]"></ng-content>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { booleanAttribute, Component, inject, Input, signal } from "@angular/core";
|
||||
import {
|
||||
booleanAttribute,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
input,
|
||||
signal,
|
||||
} from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { ScrollLayoutHostDirective } from "@bitwarden/components";
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
selector: "popup-page",
|
||||
templateUrl: "popup-page.component.html",
|
||||
@@ -13,28 +18,23 @@ import { ScrollLayoutHostDirective } from "@bitwarden/components";
|
||||
class: "tw-h-full tw-flex tw-flex-col tw-overflow-y-hidden",
|
||||
},
|
||||
imports: [CommonModule, ScrollLayoutHostDirective],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PopupPageComponent {
|
||||
protected i18nService = inject(I18nService);
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() loading = false;
|
||||
readonly loading = input<boolean>(false);
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input({ transform: booleanAttribute })
|
||||
disablePadding = false;
|
||||
readonly disablePadding = input(false, { transform: booleanAttribute });
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
protected scrolled = signal(false);
|
||||
/** Hides any overflow within the page content */
|
||||
readonly hideOverflow = input(false, { transform: booleanAttribute });
|
||||
|
||||
protected readonly scrolled = signal(false);
|
||||
isScrolled = this.scrolled.asReadonly();
|
||||
|
||||
/** Accessible loading label for the spinner. Defaults to "loading" */
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input() loadingText?: string = this.i18nService.t("loading");
|
||||
readonly loadingText = input<string | undefined>(this.i18nService.t("loading"));
|
||||
|
||||
handleScroll(event: Event) {
|
||||
this.scrolled.set((event.currentTarget as HTMLElement).scrollTop !== 0);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<section aria-hidden="true">
|
||||
<div class="tw-mt-1.5 tw-flex tw-flex-col tw-gap-4">
|
||||
<bit-skeleton-text class="tw-w-[8.625rem] tw-max-w-full tw-mb-2.5"></bit-skeleton-text>
|
||||
@for (num of numberOfItems; track $index) {
|
||||
<bit-skeleton-group class="tw-mx-2">
|
||||
<bit-skeleton class="tw-size-6" slot="start"></bit-skeleton>
|
||||
<div class="tw-flex tw-flex-col tw-gap-1">
|
||||
<bit-skeleton class="tw-w-40 tw-h-2.5 tw-max-w-full"></bit-skeleton>
|
||||
<bit-skeleton class="tw-w-24 tw-h-2.5 tw-max-w-full"></bit-skeleton>
|
||||
</div>
|
||||
</bit-skeleton-group>
|
||||
<hr class="tw-h-[1px] -tw-mr-3 tw-bg-secondary-100 tw-border-none" />
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||
|
||||
import {
|
||||
SkeletonComponent,
|
||||
SkeletonGroupComponent,
|
||||
SkeletonTextComponent,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
@Component({
|
||||
selector: "vault-loading-skeleton",
|
||||
templateUrl: "./vault-loading-skeleton.component.html",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [SkeletonGroupComponent, SkeletonComponent, SkeletonTextComponent],
|
||||
})
|
||||
export class VaultLoadingSkeletonComponent {
|
||||
protected readonly numberOfItems: null[] = new Array(15).fill(null);
|
||||
}
|
||||
Reference in New Issue
Block a user