1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-05 03:03:26 +00:00

[PM-24633] - group collections by org in individual vault filters (#16480)

* group collections by org in individual vault filters

* update vault filter

* use OrganizationId

* fix tests
This commit is contained in:
Jordan Aasen
2025-09-23 08:50:10 -07:00
committed by GitHub
parent d24524b33c
commit f642fbc4e6
4 changed files with 41 additions and 13 deletions

View File

@@ -247,6 +247,9 @@ describe("vault filter service", () => {
createCollectionView("id-3", "Collection 1/Collection 3", "org test id"),
];
collectionViews.next(storedCollections);
collectionService.groupByOrganization.mockReturnValue(
new Map([["org test id" as OrganizationId, storedCollections]]),
);
const result = await firstValueFrom(vaultFilterService.collectionTree$);
@@ -260,6 +263,9 @@ describe("vault filter service", () => {
createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"),
];
collectionViews.next(storedCollections);
collectionService.groupByOrganization.mockReturnValue(
new Map([["org test id" as OrganizationId, storedCollections]]),
);
const result = await firstValueFrom(vaultFilterService.collectionTree$);
@@ -276,6 +282,9 @@ describe("vault filter service", () => {
createCollectionView("id-4", "Collection 1/Collection 4", "org test id"),
];
collectionViews.next(storedCollections);
collectionService.groupByOrganization.mockReturnValue(
new Map([["org test id" as OrganizationId, storedCollections]]),
);
const result = await firstValueFrom(vaultFilterService.collectionTree$);
@@ -294,6 +303,9 @@ describe("vault filter service", () => {
createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"),
];
collectionViews.next(storedCollections);
collectionService.groupByOrganization.mockReturnValue(
new Map([["org test id" as OrganizationId, storedCollections]]),
);
const result = await firstValueFrom(vaultFilterService.collectionTree$);
@@ -302,7 +314,7 @@ describe("vault filter service", () => {
expect(c3.parent.node.id).toEqual("id-1");
});
it.only("calls sortDefaultCollections with the correct args", async () => {
it("calls sortDefaultCollections with the correct args", async () => {
const storedOrgs = [
createOrganization("id-defaultOrg1", "org1"),
createOrganization("id-defaultOrg2", "org2"),
@@ -326,6 +338,9 @@ describe("vault filter service", () => {
),
];
collectionViews.next(storedCollections);
collectionService.groupByOrganization.mockReturnValue(
new Map([["org test id" as OrganizationId, storedCollections]]),
);
await firstValueFrom(vaultFilterService.collectionTree$);

View File

@@ -243,22 +243,28 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
if (!collections) {
return headNode;
}
const nodes: TreeNode<CollectionFilter>[] = [];
const all: TreeNode<CollectionFilter>[] = [];
if (defaultCollectionsFlagEnabled) {
collections = sortDefaultCollections(collections, orgs, this.i18nService.collator);
}
collections.forEach((c) => {
const collectionCopy = cloneCollection(
new CollectionView({ ...c, name: c.name }),
) as CollectionFilter;
collectionCopy.icon = "bwi-collection-shared";
const parts = c.name != null ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : [];
ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, null, NestingDelimiter);
});
const groupedByOrg = this.collectionService.groupByOrganization(collections);
nodes.forEach((n) => {
for (const group of groupedByOrg.values()) {
const nodes: TreeNode<CollectionFilter>[] = [];
for (const c of group) {
const collectionCopy = cloneCollection(
new CollectionView({ ...c, name: c.name }),
) as CollectionFilter;
collectionCopy.icon = "bwi-collection-shared";
const parts = c.name ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : [];
ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, undefined, NestingDelimiter);
}
all.push(...nodes);
}
all.forEach((n) => {
n.parent = headNode;
headNode.children.push(n);
});

View File

@@ -28,4 +28,11 @@ export abstract class CollectionService {
* Transforms the input CollectionViews into TreeNodes and then returns the Treenode with the specified id
*/
abstract getNested(collections: CollectionView[], id: string): TreeNode<CollectionView>;
/*
* Groups/keys collections by OrganizationId
*/
abstract groupByOrganization(
collections: CollectionView[],
): Map<OrganizationId, CollectionView[]>;
}

View File

@@ -230,8 +230,8 @@ export class DefaultCollectionService implements CollectionService {
return all;
}
groupByOrganization(collections: CollectionView[]): Map<string, CollectionView[]> {
const groupedByOrg = new Map<string, CollectionView[]>();
groupByOrganization(collections: CollectionView[]): Map<OrganizationId, CollectionView[]> {
const groupedByOrg = new Map<OrganizationId, CollectionView[]>();
collections.map((c) => {
const key = c.organizationId;
(groupedByOrg.get(key) ?? groupedByOrg.set(key, []).get(key)!).push(c);