mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make composite not know about how children are transformed
Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
parent
c6782954b1
commit
11d2023c70
@ -3,39 +3,43 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { sortBy } from "lodash/fp";
|
|
||||||
import type { Composite } from "./get-composite";
|
import type { Composite } from "./get-composite";
|
||||||
import getComposite from "./get-composite";
|
import getCompositeFor from "./get-composite";
|
||||||
import { getCompositePaths } from "../get-composite-paths/get-composite-paths";
|
import { getCompositePaths } from "../get-composite-paths/get-composite-paths";
|
||||||
|
import { sortBy } from "lodash/fp";
|
||||||
|
|
||||||
describe("get-composite", () => {
|
describe("get-composite", () => {
|
||||||
it("given items and a specified root id, creates a composite", () => {
|
it("given items and a specified root id, creates a composite", () => {
|
||||||
|
const getComposite = getCompositeFor<{
|
||||||
|
id: string;
|
||||||
|
parentId: string | undefined;
|
||||||
|
}>({
|
||||||
|
rootId: "some-root-id",
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
|
});
|
||||||
|
|
||||||
const someRootItem = {
|
const someRootItem = {
|
||||||
someId: "some-root-id",
|
id: "some-root-id",
|
||||||
someParentId: undefined,
|
parentId: undefined,
|
||||||
someProperty: "some-root-content",
|
someProperty: "some-root-content",
|
||||||
};
|
};
|
||||||
|
|
||||||
const someItem = {
|
const someItem = {
|
||||||
someId: "some-id",
|
id: "some-id",
|
||||||
someParentId: "some-root-id",
|
parentId: "some-root-id",
|
||||||
someProperty: "some-content",
|
someProperty: "some-content",
|
||||||
};
|
};
|
||||||
|
|
||||||
const someNestedItem = {
|
const someNestedItem = {
|
||||||
someId: "some-nested-id",
|
id: "some-nested-id",
|
||||||
someParentId: "some-id",
|
parentId: "some-id",
|
||||||
someProperty: "some-nested-content",
|
someProperty: "some-nested-content",
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = [someRootItem, someItem, someNestedItem];
|
const items = [someRootItem, someItem, someNestedItem];
|
||||||
|
|
||||||
const composite = getComposite({
|
const composite = getComposite(items);
|
||||||
source: items,
|
|
||||||
rootId: "some-root-id",
|
|
||||||
getId: (x) => x.someId,
|
|
||||||
getParentId: (x) => x.someParentId,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(composite).toEqual({
|
expect(composite).toEqual({
|
||||||
id: "some-root-id",
|
id: "some-root-id",
|
||||||
@ -62,34 +66,38 @@ describe("get-composite", () => {
|
|||||||
|
|
||||||
it("given items and an unspecified root id and single item without parent as root, creates a composite", () => {
|
it("given items and an unspecified root id and single item without parent as root, creates a composite", () => {
|
||||||
const someRootItem = {
|
const someRootItem = {
|
||||||
someId: "some-root-id",
|
id: "some-root-id",
|
||||||
someProperty: "some-root-content",
|
someProperty: "some-root-content",
|
||||||
// Notice: no "someParentId" makes this the root.
|
// Notice: no "parentId" makes this the root.
|
||||||
someParentId: undefined,
|
parentId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const someItem = {
|
const someItem = {
|
||||||
someId: "some-id",
|
id: "some-id",
|
||||||
someParentId: "some-root-id",
|
parentId: "some-root-id",
|
||||||
someProperty: "some-content",
|
someProperty: "some-content",
|
||||||
};
|
};
|
||||||
|
|
||||||
const someNestedItem = {
|
const someNestedItem = {
|
||||||
someId: "some-nested-id",
|
id: "some-nested-id",
|
||||||
someParentId: "some-id",
|
parentId: "some-id",
|
||||||
someProperty: "some-nested-content",
|
someProperty: "some-nested-content",
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = [someRootItem, someItem, someNestedItem];
|
const items = [someRootItem, someItem, someNestedItem];
|
||||||
|
|
||||||
const composite = getComposite({
|
const getComposite = getCompositeFor<{
|
||||||
source: items,
|
id: string;
|
||||||
|
parentId: string | undefined;
|
||||||
|
}>({
|
||||||
// Notice: no root id
|
// Notice: no root id
|
||||||
// rootId: "some-root-id",
|
// rootId: "some-root-id",
|
||||||
getId: (x) => x.someId,
|
getId: (x) => x.id,
|
||||||
getParentId: (x) => x.someParentId,
|
getParentId: (x) => x.parentId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const composite = getComposite(items);
|
||||||
|
|
||||||
expect(composite).toEqual({
|
expect(composite).toEqual({
|
||||||
id: "some-root-id",
|
id: "some-root-id",
|
||||||
value: someRootItem,
|
value: someRootItem,
|
||||||
@ -115,27 +123,29 @@ describe("get-composite", () => {
|
|||||||
|
|
||||||
it("given items and an unspecified root id and multiple items without parent as root, throws", () => {
|
it("given items and an unspecified root id and multiple items without parent as root, throws", () => {
|
||||||
const someRootItem = {
|
const someRootItem = {
|
||||||
someId: "some-root-id",
|
id: "some-root-id",
|
||||||
// Notice: no "someParentId" makes this a root.
|
// Notice: no "parentId" makes this a root.
|
||||||
someParentId: undefined,
|
parentId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const someOtherRootItem = {
|
const someOtherRootItem = {
|
||||||
someId: "some-other-root-id",
|
id: "some-other-root-id",
|
||||||
// Notice: no "someParentId" makes also this a root.
|
// Notice: no "parentId" makes also this a root.
|
||||||
someParentId: undefined,
|
parentId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = [someRootItem, someOtherRootItem];
|
const items = [someRootItem, someOtherRootItem];
|
||||||
|
|
||||||
|
const getComposite = getCompositeFor<{
|
||||||
|
id: string;
|
||||||
|
parentId: string | undefined;
|
||||||
|
}>({
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
|
});
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
getComposite({
|
getComposite(items);
|
||||||
source: items,
|
|
||||||
// Notice: no root id
|
|
||||||
// rootId: "some-root-id",
|
|
||||||
getId: (x) => x.someId,
|
|
||||||
getParentId: (x) => x.someParentId,
|
|
||||||
});
|
|
||||||
}).toThrow(
|
}).toThrow(
|
||||||
'Tried to get a composite, but multiple roots where encountered: "some-root-id", "some-other-root-id"',
|
'Tried to get a composite, but multiple roots where encountered: "some-root-id", "some-other-root-id"',
|
||||||
);
|
);
|
||||||
@ -143,24 +153,27 @@ describe("get-composite", () => {
|
|||||||
|
|
||||||
it("given non-unique ids, throws", () => {
|
it("given non-unique ids, throws", () => {
|
||||||
const someItem = {
|
const someItem = {
|
||||||
someId: "some-id",
|
id: "some-id",
|
||||||
someParentId: "irrelevant",
|
parentId: "irrelevant",
|
||||||
};
|
};
|
||||||
|
|
||||||
const someOtherItem = {
|
const someOtherItem = {
|
||||||
someId: "some-id",
|
id: "some-id",
|
||||||
someParentId: "irrelevant",
|
parentId: "irrelevant",
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = [someItem, someOtherItem];
|
const items = [someItem, someOtherItem];
|
||||||
|
|
||||||
|
const getComposite = getCompositeFor<{
|
||||||
|
id: string;
|
||||||
|
parentId: string | undefined;
|
||||||
|
}>({
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
|
});
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
getComposite({
|
getComposite(items);
|
||||||
source: items,
|
|
||||||
rootId: "irrelevant",
|
|
||||||
getId: (x) => x.someId,
|
|
||||||
getParentId: (x) => x.someParentId,
|
|
||||||
});
|
|
||||||
}).toThrow(
|
}).toThrow(
|
||||||
'Tried to get a composite but encountered non-unique ids: "some-id"',
|
'Tried to get a composite but encountered non-unique ids: "some-id"',
|
||||||
);
|
);
|
||||||
@ -168,24 +181,27 @@ describe("get-composite", () => {
|
|||||||
|
|
||||||
it("given items with missing parent ids, when creating composite without handling for unknown parents, throws", () => {
|
it("given items with missing parent ids, when creating composite without handling for unknown parents, throws", () => {
|
||||||
const someItem = {
|
const someItem = {
|
||||||
someId: "some-id",
|
id: "some-id",
|
||||||
someParentId: undefined,
|
parentId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const someItemWithMissingParentId = {
|
const someItemWithMissingParentId = {
|
||||||
someId: "some-other-id",
|
id: "some-other-id",
|
||||||
someParentId: "some-missing-id",
|
parentId: "some-missing-id",
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = [someItem, someItemWithMissingParentId];
|
const items = [someItem, someItemWithMissingParentId];
|
||||||
|
|
||||||
|
const getComposite = getCompositeFor<{
|
||||||
|
id: string;
|
||||||
|
parentId: string | undefined;
|
||||||
|
}>({
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
|
});
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
getComposite({
|
getComposite(items);
|
||||||
source: items,
|
|
||||||
rootId: "irrelevant",
|
|
||||||
getId: (x) => x.someId,
|
|
||||||
getParentId: (x) => x.someParentId,
|
|
||||||
});
|
|
||||||
}).toThrow(
|
}).toThrow(
|
||||||
`Tried to get a composite but encountered missing parent ids: "some-missing-id".
|
`Tried to get a composite but encountered missing parent ids: "some-missing-id".
|
||||||
|
|
||||||
@ -202,7 +218,6 @@ Available parent ids are:
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const someItem = {
|
const someItem = {
|
||||||
id: "some-root-id",
|
id: "some-root-id",
|
||||||
parentId: undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const someItemWithMissingParentId = {
|
const someItemWithMissingParentId = {
|
||||||
@ -215,10 +230,16 @@ Available parent ids are:
|
|||||||
|
|
||||||
handleMissingParentIdMock = jest.fn();
|
handleMissingParentIdMock = jest.fn();
|
||||||
|
|
||||||
composite = getComposite({
|
const getComposite = getCompositeFor<{
|
||||||
source: items,
|
id: string;
|
||||||
|
parentId?: string;
|
||||||
|
}>({
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
handleMissingParentIds: handleMissingParentIdMock,
|
handleMissingParentIds: handleMissingParentIdMock,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
composite = getComposite(items);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("creates composite without the orphan item, and without throwing", () => {
|
it("creates composite without the orphan item, and without throwing", () => {
|
||||||
@ -243,151 +264,58 @@ Available parent ids are:
|
|||||||
|
|
||||||
const someRoot = {
|
const someRoot = {
|
||||||
id: "root",
|
id: "root",
|
||||||
someParentId: undefined,
|
parentId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = [someItem, someRoot];
|
const items = [someItem, someRoot];
|
||||||
|
|
||||||
|
const getComposite = getCompositeFor<{
|
||||||
|
id: string;
|
||||||
|
parentId: string | undefined;
|
||||||
|
}>({
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
|
});
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
getComposite({
|
getComposite(items);
|
||||||
source: items,
|
}).toThrow(
|
||||||
});
|
'Tried to get a composite, but found items with self as parent: "some-id"',
|
||||||
}).toThrow('Tried to get a composite, but found items with self as parent: "some-id"');
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("given undefined ids, throws", () => {
|
it("given undefined ids, throws", () => {
|
||||||
const root = {
|
const root = {
|
||||||
someParentId: undefined,
|
parentId: undefined,
|
||||||
someId: "some-root",
|
id: "some-root",
|
||||||
};
|
};
|
||||||
|
|
||||||
const someItem = {
|
const someItem = {
|
||||||
someParentId: "some-root",
|
parentId: "some-root",
|
||||||
someId: undefined,
|
id: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const someOtherItem = {
|
const someOtherItem = {
|
||||||
someParentId: "some-root",
|
parentId: "some-root",
|
||||||
someId: undefined,
|
id: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = [root, someItem, someOtherItem];
|
const items = [root, someItem, someOtherItem];
|
||||||
|
|
||||||
|
const getComposite = getCompositeFor<{
|
||||||
|
id: any;
|
||||||
|
parentId: string | undefined;
|
||||||
|
}>({
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
|
});
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
getComposite({
|
getComposite(items);
|
||||||
source: items,
|
|
||||||
rootId: "some-root",
|
|
||||||
getId: (x) => x.someId as any,
|
|
||||||
getParentId: (x) => x.someParentId,
|
|
||||||
});
|
|
||||||
}).toThrow("Tried to get a composite but encountered 2 undefined ids");
|
}).toThrow("Tried to get a composite but encountered 2 undefined ids");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("given items with default properties for id and parentId, creates a composite", () => {
|
it("given transformed children, creates a composite with transformed children", () => {
|
||||||
const someRootItem = {
|
|
||||||
id: "some-root-id",
|
|
||||||
};
|
|
||||||
|
|
||||||
const someItem = {
|
|
||||||
id: "some-id",
|
|
||||||
parentId: "some-root-id",
|
|
||||||
};
|
|
||||||
|
|
||||||
const someNestedItem = {
|
|
||||||
id: "some-nested-id",
|
|
||||||
parentId: "some-id",
|
|
||||||
};
|
|
||||||
|
|
||||||
const items = [someRootItem, someItem, someNestedItem];
|
|
||||||
|
|
||||||
const composite = getComposite({
|
|
||||||
source: items,
|
|
||||||
// Notice: no need for functions
|
|
||||||
// getId: (x) => x.id,
|
|
||||||
// getParentId: (x) => x.parentId,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(composite).toEqual({
|
|
||||||
id: "some-root-id",
|
|
||||||
value: someRootItem,
|
|
||||||
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: "some-id",
|
|
||||||
parentId: "some-root-id",
|
|
||||||
value: someItem,
|
|
||||||
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: "some-nested-id",
|
|
||||||
parentId: "some-id",
|
|
||||||
value: someNestedItem,
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("given explicitly ordered items, creates a composite with ordered children", () => {
|
|
||||||
const someRootItem = {
|
|
||||||
id: "some-root-id",
|
|
||||||
someOrderNumber: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const someItem1 = {
|
|
||||||
id: "some-id-1",
|
|
||||||
parentId: "some-root-id",
|
|
||||||
someOrderNumber: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const someItem2 = {
|
|
||||||
id: "some-id-2",
|
|
||||||
parentId: "some-root-id",
|
|
||||||
someOrderNumber: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const someChildItem1 = {
|
|
||||||
id: "some-child-id-1",
|
|
||||||
parentId: "some-id-1",
|
|
||||||
someOrderNumber: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const someChildItem2 = {
|
|
||||||
id: "some-child-id-2",
|
|
||||||
parentId: "some-id-1",
|
|
||||||
someOrderNumber: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const items = [
|
|
||||||
someRootItem,
|
|
||||||
// Note: not in order yet.
|
|
||||||
someItem2,
|
|
||||||
someItem1,
|
|
||||||
someChildItem2,
|
|
||||||
someChildItem1,
|
|
||||||
];
|
|
||||||
|
|
||||||
const composite = getComposite({
|
|
||||||
source: items,
|
|
||||||
// Note: this is the explicit function to order a composite's children.
|
|
||||||
getOrderedChildren: (things) =>
|
|
||||||
sortBy((thing) => thing.someOrderNumber, things),
|
|
||||||
});
|
|
||||||
|
|
||||||
const orderedPaths = getCompositePaths(composite);
|
|
||||||
|
|
||||||
expect(orderedPaths).toEqual([
|
|
||||||
["some-root-id"],
|
|
||||||
["some-root-id", "some-id-1"],
|
|
||||||
["some-root-id", "some-id-1", "some-child-id-1"],
|
|
||||||
["some-root-id", "some-id-1", "some-child-id-2"],
|
|
||||||
["some-root-id", "some-id-2"],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("given implicitly ordered items, creates a composite with ordered children", () => {
|
|
||||||
const someRootItem = {
|
const someRootItem = {
|
||||||
id: "some-root-id",
|
id: "some-root-id",
|
||||||
orderNumber: 1,
|
orderNumber: 1,
|
||||||
@ -426,12 +354,19 @@ Available parent ids are:
|
|||||||
someChildItem1,
|
someChildItem1,
|
||||||
];
|
];
|
||||||
|
|
||||||
const composite = getComposite({
|
const getComposite = getCompositeFor<{
|
||||||
source: items,
|
id: string;
|
||||||
// Note: without explicit getOrderedChildren for ordering, implicit default value of "orderNumber" will be used, if it exists.
|
parentId?: string;
|
||||||
// getOrderedChildren: things => sortBy(thing => thing.orderNumber, things),
|
orderNumber?: number;
|
||||||
|
}>({
|
||||||
|
getId: (x) => x.id,
|
||||||
|
getParentId: (x) => x.parentId,
|
||||||
|
transformChildren: (things) =>
|
||||||
|
sortBy((thing) => thing.orderNumber, things),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const composite = getComposite(items);
|
||||||
|
|
||||||
const orderedPaths = getCompositePaths(composite);
|
const orderedPaths = getCompositePaths(composite);
|
||||||
|
|
||||||
expect(orderedPaths).toEqual([
|
expect(orderedPaths).toEqual([
|
||||||
|
|||||||
@ -13,8 +13,7 @@ import {
|
|||||||
uniq,
|
uniq,
|
||||||
without,
|
without,
|
||||||
compact,
|
compact,
|
||||||
get,
|
identity,
|
||||||
sortBy,
|
|
||||||
} from "lodash/fp";
|
} from "lodash/fp";
|
||||||
|
|
||||||
export interface Composite<T> {
|
export interface Composite<T> {
|
||||||
@ -24,21 +23,21 @@ export interface Composite<T> {
|
|||||||
children: Composite<T>[];
|
children: Composite<T>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default <T>({
|
interface Configuration<T> {
|
||||||
source,
|
|
||||||
rootId,
|
|
||||||
getId = get("id"),
|
|
||||||
getParentId = get("parentId"),
|
|
||||||
getOrderedChildren = (things: T[]) => sortBy("orderNumber", things),
|
|
||||||
handleMissingParentIds = throwMissingParentIds,
|
|
||||||
}: {
|
|
||||||
source: T[];
|
|
||||||
rootId?: string;
|
rootId?: string;
|
||||||
getId?: (thing: T) => string;
|
getId: (thing: T) => string;
|
||||||
getParentId?: (thing: T) => string | undefined;
|
getParentId: (thing: T) => string | undefined;
|
||||||
getOrderedChildren?: (things: T[]) => T[];
|
transformChildren?: (things: T[]) => T[];
|
||||||
handleMissingParentIds?: (parentIdsForHandling: ParentIdsForHandling) => void;
|
handleMissingParentIds?: (parentIdsForHandling: ParentIdsForHandling) => void;
|
||||||
}) => {
|
}
|
||||||
|
|
||||||
|
export default <T>({
|
||||||
|
rootId = undefined,
|
||||||
|
getId,
|
||||||
|
getParentId,
|
||||||
|
transformChildren = identity,
|
||||||
|
handleMissingParentIds = throwMissingParentIds,
|
||||||
|
}: Configuration<T>) => (source: T[]) => {
|
||||||
const undefinedIds = pipeline(
|
const undefinedIds = pipeline(
|
||||||
source,
|
source,
|
||||||
filter((x) => getId(x) === undefined),
|
filter((x) => getId(x) === undefined),
|
||||||
@ -107,7 +106,7 @@ export default <T>({
|
|||||||
return parentId !== undefined && parentId === thingId;
|
return parentId !== undefined && parentId === thingId;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getOrderedChildren,
|
transformChildren,
|
||||||
|
|
||||||
map(toComposite),
|
map(toComposite),
|
||||||
),
|
),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user