Skip to content

Commit c62be94

Browse files
committed
restructure scroll containers on popup page and vault
1 parent 65b4194 commit c62be94

File tree

7 files changed

+107
-87
lines changed

7 files changed

+107
-87
lines changed

apps/browser/src/platform/popup/layout/popup-layout.mdx

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,21 +174,51 @@ When the browser extension is popped out, the "popout" button should not be pass
174174

175175
## With Virtual Scroll
176176

177-
If you are using a virtual scrolling container inside of the popup page (aka replacing the default
178-
popup page scrolling container with a virtual scroll container), you'll want to configure the
179-
component in the following ways:
180-
181-
- Use the `disablePadding` input on the `popup-page`
182-
- Add padding and scrollbar to the virtual scrolling element to match the default behavior of the
183-
popup page scroll container, which ensures that the scrollbar is at the far right edge of the
184-
popup
185-
- Add max width to the child of the virtual scroll element, matching the max width breakpoints used
186-
in the `popup-page`
177+
If you are using a virtual scrolling container inside of the popup page, you'll want to apply the
178+
`bitScrollLayout` directive to the `cdk-virtual-scroll-viewport` element. This tells the virtual
179+
scroll viewport to use the popup page's scroll layout div as the scrolling container.
187180

188181
See the code in the example below.
189182

190183
<Canvas of={stories.WithVirtualScrollChild} />
191184

185+
### Known Virtual Scroll Issues
186+
187+
Virtual scroll will not work in this specific case, where the `bitScrollLayout` is a child of the
188+
`popup-page` within the same template file:
189+
190+
```
191+
<popup-page>
192+
<cdk-virtual-scroll-viewport bitScrollLayout>
193+
// virtual scroll implementation here
194+
</div>
195+
</popup-page>
196+
```
197+
198+
This is due to the way Angular initializes components. In this particular composition, the child
199+
gets constructed before the `popup-page` and thus has no scroll container to reference. Workarounds
200+
are:
201+
202+
1. Wrap the child in another component
203+
204+
```
205+
<popup-page>
206+
<component-that-contains-bitScrollLayout></component-that-contains-bitScrollLayout>
207+
</popup-page>
208+
```
209+
210+
2. Use a `defer` block
211+
212+
```
213+
<popup-page>
214+
@defer (on immediate) {
215+
<cdk-virtual-scroll-viewport bitScrollLayout>
216+
// virtual scroll implementation here
217+
</div>
218+
}
219+
</popup-page>
220+
```
221+
192222
# Other stories
193223

194224
## Centered Content

apps/browser/src/platform/popup/layout/popup-layout.stories.ts

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
NoItemsModule,
2020
SearchModule,
2121
SectionComponent,
22+
ScrollLayoutDirective,
2223
} from "@bitwarden/components";
2324

2425
import { PopupRouterCacheService } from "../view-cache/popup-router-cache.service";
@@ -318,6 +319,7 @@ export default {
318319
decorators: [
319320
moduleMetadata({
320321
imports: [
322+
ScrollLayoutDirective,
321323
PopupTabNavigationComponent,
322324
PopupHeaderComponent,
323325
PopupPageComponent,
@@ -656,53 +658,48 @@ export const WithVirtualScrollChild: Story = {
656658
props: { ...args, data: Array.from(Array(20).keys()) },
657659
template: /* HTML */ `
658660
<extension-popped-container>
659-
<popup-page disablePadding>
661+
<popup-page>
660662
<popup-header slot="header" pageTitle="Test"> </popup-header>
661663
<mock-search slot="above-scroll-area"></mock-search>
662-
<div
663-
cdkVirtualScrollingElement
664-
class="tw-h-full tw-p-3 bit-compact:tw-p-2 tw-styled-scrollbar"
665-
>
666-
<div class="tw-max-w-screen-sm tw-mx-auto">
667-
<bit-section>
668-
<bit-item-group aria-label="Mock Vault Items">
669-
<cdk-virtual-scroll-viewport itemSize="55">
670-
<bit-item *cdkVirtualFor="let item of data; index as i">
671-
<button type="button" bit-item-content>
672-
<i
673-
slot="start"
674-
class="bwi bwi-globe tw-text-3xl tw-text-muted"
675-
aria-hidden="true"
676-
></i>
677-
{{ i }} of {{ data.length - 1 }}
678-
<span slot="secondary">Bar</span>
679-
</button>
680-
681-
<ng-container slot="end">
682-
<bit-item-action>
683-
<button type="button" bitBadge variant="primary">Fill</button>
684-
</bit-item-action>
685-
<bit-item-action>
686-
<button
687-
type="button"
688-
bitIconButton="bwi-clone"
689-
aria-label="Copy item"
690-
></button>
691-
</bit-item-action>
692-
<bit-item-action>
693-
<button
694-
type="button"
695-
bitIconButton="bwi-ellipsis-v"
696-
aria-label="More options"
697-
></button>
698-
</bit-item-action>
699-
</ng-container>
700-
</bit-item>
701-
</cdk-virtual-scroll-viewport>
702-
</bit-item-group>
703-
</bit-section>
704-
</div>
705-
</div>
664+
<bit-section>
665+
@defer (on immediate) {
666+
<bit-item-group aria-label="Mock Vault Items">
667+
<cdk-virtual-scroll-viewport itemSize="61" bitScrollLayout>
668+
<bit-item *cdkVirtualFor="let item of data; index as i">
669+
<button type="button" bit-item-content>
670+
<i
671+
slot="start"
672+
class="bwi bwi-globe tw-text-3xl tw-text-muted"
673+
aria-hidden="true"
674+
></i>
675+
{{ i }} of {{ data.length - 1 }}
676+
<span slot="secondary">Bar</span>
677+
</button>
678+
679+
<ng-container slot="end">
680+
<bit-item-action>
681+
<button type="button" bitBadge variant="primary">Fill</button>
682+
</bit-item-action>
683+
<bit-item-action>
684+
<button
685+
type="button"
686+
bitIconButton="bwi-clone"
687+
aria-label="Copy item"
688+
></button>
689+
</bit-item-action>
690+
<bit-item-action>
691+
<button
692+
type="button"
693+
bitIconButton="bwi-ellipsis-v"
694+
aria-label="More options"
695+
></button>
696+
</bit-item-action>
697+
</ng-container>
698+
</bit-item>
699+
</cdk-virtual-scroll-viewport>
700+
</bit-item-group>
701+
}
702+
</bit-section>
706703
</popup-page>
707704
</extension-popped-container>
708705
`,

apps/browser/src/platform/popup/layout/popup-page.component.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,16 @@
1414
</div>
1515
</div>
1616
<div
17-
class="tw-mx-auto tw-overflow-y-auto tw-flex tw-flex-col tw-size-full tw-styled-scrollbar"
17+
class="tw-mx-auto tw-overflow-y-auto tw-size-full tw-styled-scrollbar"
1818
data-testid="popup-layout-scroll-region"
1919
(scroll)="handleScroll($event)"
2020
[ngClass]="{
2121
'tw-invisible': loading,
2222
'tw-p-3 bit-compact:tw-p-2': !disablePadding,
2323
}"
24+
bitScrollLayoutHost
2425
>
25-
<div
26-
class="has-[[cdkvirtualscrollingelement]]:tw-max-w-full tw-max-w-screen-sm tw-mx-auto tw-flex-1 tw-flex tw-flex-col tw-w-full"
27-
>
26+
<div class="tw-max-w-screen-sm tw-mx-auto tw-flex tw-flex-col tw-w-full">
2827
<ng-content></ng-content>
2928
</div>
3029
</div>

apps/browser/src/platform/popup/layout/popup-page.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CommonModule } from "@angular/common";
22
import { booleanAttribute, Component, inject, Input, signal } from "@angular/core";
33

44
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
5+
import { ScrollLayoutHostDirective } from "@bitwarden/components";
56

67
@Component({
78
selector: "popup-page",
@@ -10,7 +11,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
1011
host: {
1112
class: "tw-h-full tw-flex tw-flex-col tw-overflow-y-hidden",
1213
},
13-
imports: [CommonModule],
14+
imports: [CommonModule, ScrollLayoutHostDirective],
1415
})
1516
export class PopupPageComponent {
1617
protected i18nService = inject(I18nService);

apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,7 @@ <h3 class="tw-text-muted tw-text-xs tw-font-semibold tw-pl-1 tw-mb-1 bit-compact
8989
</h3>
9090
</ng-container>
9191

92-
<cdk-virtual-scroll-viewport
93-
[itemSize]="itemHeight$ | async"
94-
class="tw-overflow-visible [&>.cdk-virtual-scroll-content-wrapper]:[contain:layout_style]"
95-
>
92+
<cdk-virtual-scroll-viewport [itemSize]="itemHeight$ | async" bitScrollLayout>
9693
<bit-item *cdkVirtualFor="let cipher of group.ciphers">
9794
<button
9895
bit-item-content

apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
SectionComponent,
4343
SectionHeaderComponent,
4444
TypographyModule,
45+
ScrollLayoutDirective,
4546
} from "@bitwarden/components";
4647
import {
4748
DecryptionFailureDialogComponent,
@@ -74,6 +75,7 @@ import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options
7475
ScrollingModule,
7576
DisclosureComponent,
7677
DisclosureTriggerForDirective,
78+
ScrollLayoutDirective,
7779
],
7880
selector: "app-vault-list-items-container",
7981
templateUrl: "vault-list-items-container.component.html",

apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<popup-page [loading]="loading$ | async" disablePadding>
1+
<popup-page [loading]="loading$ | async">
22
<popup-header slot="header" [pageTitle]="'vault' | i18n">
33
<ng-container slot="end">
44
<app-new-item-dropdown [initialValues]="newItemItemValues$ | async"></app-new-item-dropdown>
@@ -86,27 +86,21 @@
8686
</div>
8787
</div>
8888

89-
<div
90-
*ngIf="vaultState === null"
91-
cdkVirtualScrollingElement
92-
class="tw-h-full tw-p-3 bit-compact:tw-p-2 tw-styled-scrollbar"
93-
>
94-
<div class="tw-max-w-screen-sm tw-mx-auto">
95-
<app-autofill-vault-list-items></app-autofill-vault-list-items>
96-
<app-vault-list-items-container
97-
[title]="'favorites' | i18n"
98-
[ciphers]="(favoriteCiphers$ | async) || []"
99-
id="favorites"
100-
collapsibleKey="favorites"
101-
></app-vault-list-items-container>
102-
<app-vault-list-items-container
103-
[title]="'allItems' | i18n"
104-
[ciphers]="(remainingCiphers$ | async) || []"
105-
id="allItems"
106-
disableSectionMargin
107-
collapsibleKey="allItems"
108-
></app-vault-list-items-container>
109-
</div>
110-
</div>
89+
<ng-container *ngIf="vaultState === null">
90+
<app-autofill-vault-list-items></app-autofill-vault-list-items>
91+
<app-vault-list-items-container
92+
[title]="'favorites' | i18n"
93+
[ciphers]="(favoriteCiphers$ | async) || []"
94+
id="favorites"
95+
collapsibleKey="favorites"
96+
></app-vault-list-items-container>
97+
<app-vault-list-items-container
98+
[title]="'allItems' | i18n"
99+
[ciphers]="(remainingCiphers$ | async) || []"
100+
id="allItems"
101+
disableSectionMargin
102+
collapsibleKey="allItems"
103+
></app-vault-list-items-container>
104+
</ng-container>
111105
</ng-container>
112106
</popup-page>

0 commit comments

Comments
 (0)