mirror of
https://github.com/bendtherules/react-fiber-traverse.git
synced 2026-08-18 22:01:49 +00:00
match - Add css match functions with basic test cases
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
findNodeByComponentRef,
|
||||
findNodeByComponentName
|
||||
} from "./findNode";
|
||||
import { matchGenerator, matchAll, matchFirst } from "./match";
|
||||
import * as Utils from "./utils";
|
||||
|
||||
export {
|
||||
@@ -12,5 +13,8 @@ export {
|
||||
findNodeByComponent,
|
||||
findNodeByComponentRef,
|
||||
findNodeByComponentName,
|
||||
matchGenerator,
|
||||
matchAll,
|
||||
matchFirst,
|
||||
Utils
|
||||
};
|
||||
|
||||
+78
-15
@@ -1,20 +1,22 @@
|
||||
import CSSwhat from "css-what";
|
||||
import * as CSSWhat from "css-what";
|
||||
import { traverseGenerator } from "./traverse";
|
||||
import { FiberNode } from "./mocked-types";
|
||||
import { isNodeNotHtmlLike } from "./utils";
|
||||
|
||||
function* matchGenerator(
|
||||
node: FiberNode,
|
||||
match: string | CSSwhat.Selector[][]
|
||||
match: string | CSSWhat.Selector[][]
|
||||
): IterableIterator<FiberNode> {
|
||||
// Either parse match string or allow parsed match object as it is
|
||||
let matchParsed: CSSwhat.Selector[][];
|
||||
let matchParsed: CSSWhat.Selector[][];
|
||||
if (typeof match === "string") {
|
||||
matchParsed = CSSwhat.parse(match);
|
||||
matchParsed = CSSWhat.parse(match, {
|
||||
lowerCaseTags: false,
|
||||
lowerCaseAttributeNames: false
|
||||
});
|
||||
} else {
|
||||
matchParsed = match;
|
||||
}
|
||||
|
||||
// If selector is a combination of multiple basic selectors (a, b),
|
||||
// pass them separately to matchGenerator and combine their results one after another
|
||||
const selectorsCount = matchParsed.length;
|
||||
@@ -35,20 +37,46 @@ function* matchGenerator(
|
||||
|
||||
let currentMatchingNodes: FiberNode[] = [node];
|
||||
let currentMatchingSelectorPartIndex = 0;
|
||||
let currentMatchingSelectorPart: CSSwhat.Selector =
|
||||
parsedSelector[currentMatchingSelectorPartIndex];
|
||||
let lastRelationshipSelectorPart: CSSwhat.Selector | undefined = undefined;
|
||||
let lastRelationshipSelectorPart: CSSWhat.Selector | undefined = undefined;
|
||||
|
||||
while (currentMatchingSelectorPartIndex < parsedSelector.length) {
|
||||
const nextMatchingNodes: FiberNode[] = [];
|
||||
|
||||
for (const currentNode of currentMatchingNodes) {
|
||||
const currentMatchingSelectorPart: CSSWhat.Selector =
|
||||
parsedSelector[currentMatchingSelectorPartIndex];
|
||||
if (["tag"].includes(currentMatchingSelectorPart.type)) {
|
||||
const traverseIterator = traverseGenerator(currentNode);
|
||||
const startParams = {
|
||||
skipSelfForStartNode: true,
|
||||
skipSiblingForStartNode: true
|
||||
};
|
||||
|
||||
let nextParams: {
|
||||
skipChild?: boolean;
|
||||
skipSibling?: boolean;
|
||||
} = {};
|
||||
if (
|
||||
lastRelationshipSelectorPart === undefined ||
|
||||
lastRelationshipSelectorPart.type === "descendant"
|
||||
) {
|
||||
nextParams = { skipChild: false, skipSibling: false };
|
||||
} else if (lastRelationshipSelectorPart.type === "child") {
|
||||
// visit only first level of child
|
||||
nextParams = { skipChild: true };
|
||||
} else if (lastRelationshipSelectorPart.type === "sibling") {
|
||||
// visit only siblings of start node
|
||||
nextParams = { skipChild: true, skipSibling: true };
|
||||
startParams.skipSiblingForStartNode = false;
|
||||
}
|
||||
|
||||
const traverseIterator = traverseGenerator(currentNode, startParams);
|
||||
|
||||
// Handle supported non-traversal parts here
|
||||
if (currentMatchingSelectorPart.type == "tag") {
|
||||
for (const tmpNode of traverseIterator) {
|
||||
let tmpNode: FiberNode;
|
||||
while (
|
||||
!({ value: tmpNode } = traverseIterator.next(nextParams)).done
|
||||
) {
|
||||
if (
|
||||
isNodeNotHtmlLike(tmpNode) &&
|
||||
tmpNode.type.name === currentMatchingSelectorPart.name
|
||||
@@ -57,11 +85,15 @@ function* matchGenerator(
|
||||
}
|
||||
}
|
||||
}
|
||||
traverseIterator.throw &&
|
||||
traverseIterator.throw(new Error("cleanup"));
|
||||
} else if (["descendant"].includes(currentMatchingSelectorPart.type)) {
|
||||
// Handle traversal parts here
|
||||
// traverseIterator.throw &&
|
||||
// traverseIterator.throw(new Error("cleanup"));
|
||||
} else if (
|
||||
["descendant", "child"].includes(currentMatchingSelectorPart.type)
|
||||
) {
|
||||
// Handle traversal parts here - Save for look back in next part
|
||||
lastRelationshipSelectorPart = currentMatchingSelectorPart;
|
||||
// Preserve currentMatchingNodes
|
||||
nextMatchingNodes.push(...currentMatchingNodes);
|
||||
} else {
|
||||
// For unhandled parts
|
||||
lastRelationshipSelectorPart = undefined;
|
||||
@@ -69,10 +101,41 @@ function* matchGenerator(
|
||||
}
|
||||
|
||||
currentMatchingNodes = nextMatchingNodes;
|
||||
currentMatchingSelectorPartIndex += 1;
|
||||
}
|
||||
|
||||
for (const tmpNode of currentMatchingNodes) {
|
||||
yield tmpNode;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export { matchGenerator };
|
||||
function matchAll(
|
||||
node: FiberNode,
|
||||
match: string | CSSWhat.Selector[][]
|
||||
): Array<FiberNode> {
|
||||
return [...matchGenerator(node, match)];
|
||||
}
|
||||
|
||||
function matchFirst(
|
||||
node: FiberNode,
|
||||
match: string | CSSWhat.Selector[][]
|
||||
): FiberNode | null {
|
||||
const matchIterator = matchGenerator(node, match);
|
||||
const firstResult = matchIterator.next();
|
||||
|
||||
// Cancel generator
|
||||
matchIterator.throw && matchIterator.throw(new Error("Cleanup"));
|
||||
|
||||
// If match found, return that
|
||||
if (!firstResult.done) {
|
||||
return firstResult.value;
|
||||
}
|
||||
|
||||
// Else return null
|
||||
return null;
|
||||
}
|
||||
|
||||
export { matchGenerator, matchAll, matchFirst };
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import * as React from "react";
|
||||
|
||||
// Import stuff from src
|
||||
import {
|
||||
// matchGenerator,
|
||||
matchAll
|
||||
// matchFirst
|
||||
} from "../src";
|
||||
|
||||
import {
|
||||
FiberNodeForComponentClass
|
||||
// FiberNodeForInstrinsicElement,
|
||||
// FiberNodeForFunctionComponent
|
||||
} from "../src/mocked-types";
|
||||
|
||||
// Import test helpers and sample components
|
||||
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
|
||||
import {
|
||||
// createClassComponents,
|
||||
// createFunctionComponents,
|
||||
createClassComponent,
|
||||
createClassComponents
|
||||
} from "./utils/createComponent";
|
||||
|
||||
describe("matchGenerator", () => {
|
||||
let container: HTMLDivElement;
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.body.appendChild(document.createElement("div"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
describe("basic", () => {
|
||||
it("should work for depth=1 class", () => {
|
||||
const C1 = createClassComponent("C1");
|
||||
function CRoot() {
|
||||
return <C1 />;
|
||||
}
|
||||
const rootNode = mountAndGetRootNode(CRoot, container);
|
||||
|
||||
const nodes = matchAll(rootNode, "C1");
|
||||
|
||||
// Can't be zero, in any case
|
||||
expect(nodes.length).not.toBe(0);
|
||||
// Check that only one node is yielded
|
||||
expect(nodes.length).toBe(1);
|
||||
// Check that type is correct
|
||||
expect((nodes[0] as FiberNodeForComponentClass).type).toBe(C1);
|
||||
});
|
||||
|
||||
it("should work for depth=2 class", () => {
|
||||
const [C1, C2] = createClassComponents(["C1", "C2"]);
|
||||
function CRoot() {
|
||||
return (
|
||||
<C1>
|
||||
<C2 />
|
||||
</C1>
|
||||
);
|
||||
}
|
||||
const rootNode = mountAndGetRootNode(CRoot, container);
|
||||
|
||||
const nodes = matchAll(rootNode, "C2");
|
||||
|
||||
// Can't be zero, in any case
|
||||
expect(nodes.length).not.toBe(0);
|
||||
// Check that only one node is yielded
|
||||
expect(nodes.length).toBe(1);
|
||||
// Check that type is correct
|
||||
expect((nodes[0] as FiberNodeForComponentClass).type).toBe(C2);
|
||||
expect((nodes[0] as FiberNodeForComponentClass).type).not.toBe(C1);
|
||||
});
|
||||
|
||||
it("should work for depth=2 class with descendant selector", () => {
|
||||
const [C1, C2] = createClassComponents(["C1", "C2"]);
|
||||
function CRoot() {
|
||||
return (
|
||||
<C1>
|
||||
<C2 />
|
||||
</C1>
|
||||
);
|
||||
}
|
||||
const rootNode = mountAndGetRootNode(CRoot, container);
|
||||
|
||||
const nodes = matchAll(rootNode, "C1 C2");
|
||||
|
||||
// Can't be zero, in any case
|
||||
expect(nodes.length).not.toBe(0);
|
||||
// Check that only one node is yielded
|
||||
expect(nodes.length).toBe(1);
|
||||
// Check that type is correct
|
||||
expect((nodes[0] as FiberNodeForComponentClass).type).toBe(C2);
|
||||
expect((nodes[0] as FiberNodeForComponentClass).type).not.toBe(C1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user