Skip to content
Snippets Groups Projects
Unverified Commit 7fc4fc7c authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Hide collections without datasets in dataset picker (#19153)

* Add tests for buildCollectionTree

* Rename test objects for better readability

* Allow filtering collection tree items by models

* Hide collections without datasets in dataset picker

* Keep parent nodes if they don't have target models
parent be0dc4e6
No related branches found
No related tags found
No related merge requests found
......@@ -57,6 +57,8 @@ function SavedQuestionPicker({
collectionName,
}) {
const collectionTree = useMemo(() => {
const targetModels = isDatasets ? ["dataset"] : null;
const preparedCollections = [];
const userPersonalCollections = currentUserPersonalCollections(
collections,
......@@ -86,9 +88,9 @@ function SavedQuestionPicker({
return [
OUR_ANALYTICS_COLLECTION,
...buildCollectionTree(preparedCollections),
...buildCollectionTree(preparedCollections, { targetModels }),
];
}, [collections, currentUser]);
}, [collections, currentUser, isDatasets]);
const initialCollection = useMemo(
() => findCollectionByName(collectionTree, collectionName),
......
import _ from "underscore";
import { getCollectionIcon } from "metabase/entities/collections";
export function buildCollectionTree(collections) {
function hasIntersection(list1, list2) {
return _.intersection(list1, list2).length > 0;
}
export function buildCollectionTree(collections, { targetModels } = {}) {
if (collections == null) {
return [];
}
return collections.map(collection => ({
id: collection.id,
name: collection.name,
schemaName: collection.originalName || collection.name,
icon: getCollectionIcon(collection),
children: buildCollectionTree(collection.children),
}));
const shouldFilterCollections = Array.isArray(targetModels);
return collections.flatMap(collection => {
const hasTargetModels =
!shouldFilterCollections ||
hasIntersection(targetModels, collection.below) ||
hasIntersection(targetModels, collection.here);
return hasTargetModels
? {
id: collection.id,
name: collection.name,
schemaName: collection.originalName || collection.name,
icon: getCollectionIcon(collection),
children: buildCollectionTree(collection.children, { targetModels }),
}
: [];
});
}
export const findCollectionByName = (collections, name) => {
......
import { setupEnterpriseTest } from "__support__/enterprise";
import { buildCollectionTree } from "./utils";
function getCollection({
id = 1,
name = "Collection",
children = [],
...rest
} = {}) {
return {
id,
name,
children,
...rest,
};
}
describe("buildCollectionTree", () => {
it("returns an empty array when collections are not passed", () => {
expect(buildCollectionTree()).toEqual([]);
});
it("correctly transforms collections", () => {
const collection = getCollection({ children: [] });
const [transformed] = buildCollectionTree([collection]);
expect(transformed).toEqual({
id: collection.id,
name: collection.name,
schemaName: collection.name,
icon: { name: "folder" },
children: [],
});
});
it("prefers originalName over name for schema names", () => {
const collection = getCollection({ name: "bar", originalName: "foo" });
const [transformed] = buildCollectionTree([collection]);
expect(transformed.schemaName).toBe(collection.originalName);
});
it("recursively transforms collection children", () => {
const grandchild = getCollection({ id: 3, name: "C3" });
const child = getCollection({
id: 2,
name: "C2",
children: [grandchild],
});
const collection = getCollection({
id: 1,
name: "C1",
children: [child],
});
const [transformed] = buildCollectionTree([collection]);
expect(transformed).toEqual({
id: collection.id,
name: collection.name,
schemaName: collection.name,
icon: { name: "folder" },
children: [
{
id: child.id,
name: child.name,
schemaName: child.name,
icon: { name: "folder" },
children: [
{
id: grandchild.id,
name: grandchild.name,
schemaName: grandchild.name,
icon: { name: "folder" },
children: [],
},
],
},
],
});
});
it("returns regular icon for official collections in OSS", () => {
const collection = getCollection({ authority_level: "official" });
const [transformed] = buildCollectionTree([collection]);
expect(transformed.icon).toEqual({ name: "folder" });
});
describe("filtering by models", () => {
it("only keeps collection branches containing target models", () => {
const grandchild1 = getCollection({
id: 4,
name: "Grandchild 1",
here: ["dataset"],
});
const grandchild2 = getCollection({
id: 3,
name: "Grandchild 2",
here: ["card"],
});
const child = getCollection({
id: 2,
name: "Child",
below: ["dataset", "card"],
children: [grandchild1, grandchild2],
});
const collection = getCollection({
id: 1,
name: "Top-level",
below: ["dataset", "card"],
children: [child],
});
const transformed = buildCollectionTree([collection], {
targetModels: ["dataset"],
});
expect(transformed).toEqual([
{
id: collection.id,
name: collection.name,
schemaName: collection.name,
icon: { name: "folder" },
children: [
{
id: child.id,
name: child.name,
schemaName: child.name,
icon: { name: "folder" },
children: [
{
id: grandchild1.id,
name: grandchild1.name,
schemaName: grandchild1.name,
icon: { name: "folder" },
children: [],
},
],
},
],
},
]);
});
it("filters top-level collections not containing target models", () => {
const collectionWithDatasets = getCollection({
id: 1,
name: "Top-level",
here: ["dataset"],
children: [],
});
const collectionWithCards = getCollection({
id: 5,
name: "Top-level 2",
below: ["card"],
});
const transformed = buildCollectionTree(
[collectionWithDatasets, collectionWithCards],
{
targetModels: ["dataset"],
},
);
expect(transformed).toEqual([
{
id: collectionWithDatasets.id,
name: collectionWithDatasets.name,
schemaName: collectionWithDatasets.name,
icon: { name: "folder" },
children: [],
},
]);
});
it("doesn't filter collections if targetModels are not passed", () => {
const child = getCollection({ id: 2, name: "Child", here: ["dataset"] });
const collection = getCollection({
id: 1,
name: "Top-level",
below: ["dataset"],
children: [child],
});
const collectionWithCards = getCollection({
id: 5,
name: "Top-level 2",
below: ["card"],
});
const transformed = buildCollectionTree([
collection,
collectionWithCards,
]);
expect(transformed).toEqual([
{
id: collection.id,
name: collection.name,
schemaName: collection.name,
icon: { name: "folder" },
children: [
{
id: child.id,
name: child.name,
schemaName: child.name,
icon: { name: "folder" },
children: [],
},
],
},
{
id: collectionWithCards.id,
name: collectionWithCards.name,
schemaName: collectionWithCards.name,
icon: { name: "folder" },
children: [],
},
]);
});
});
describe("EE", () => {
beforeEach(() => {
setupEnterpriseTest();
});
it("returns correct icon for official collections", () => {
const collection = getCollection({ authority_level: "official" });
const [transformed] = buildCollectionTree([collection]);
expect(transformed.icon).toEqual({
color: expect.any(String),
name: "badge",
tooltip: "Official collection",
});
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment