Skip to content

Commit

Permalink
Merge pull request #504 from 2taesung/main
Browse files Browse the repository at this point in the history
#493 conflict 해결 pr
  • Loading branch information
2taesung committed Oct 3, 2023
2 parents ea57676 + bd0afd6 commit 301cfeb
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { selectedDataUpdater } from "../VerticalClusterList.util";

import { CLUSTER_HEIGHT, DETAIL_HEIGHT, GRAPH_WIDTH, NODE_GAP, SVG_MARGIN } from "./ClusterGraph.const";
import type { ClusterGraphElement } from "./ClusterGraph.type";
import { destroyClusterGraph, drawClusterBox, drawCommitAmountCluster, drawTotalLine } from "./Draws";
import { destroyClusterGraph, drawClusterBox, drawCommitAmountCluster, drawSubGraph, drawTotalLine } from "./Draws";
import { getTranslateAfterSelect } from "./ClusterGraph.util";
import styles from "./ClusterGraph.module.scss";

Expand All @@ -28,46 +28,49 @@ const drawClusterGraph = (
.data(data)
.join("g")
.on("click", onClickCluster)
.attr("data-testid", "cluster-graph__container")
.attr("class", cx("cluster-graph__container"))
.attr("transform", (d, i) => getTranslateAfterSelect(d, i, detailElementHeight, true));
group.append("title").text((_, i) => `${i + 1}번째 container`);

group.append("title").text((_, i) => `${i + 1} container`);

group
.transition()
.duration(0)
.attr("transform", (d, i) => getTranslateAfterSelect(d, i, detailElementHeight));

drawTotalLine(svgRef, data, detailElementHeight, SVG_MARGIN, CLUSTER_HEIGHT, NODE_GAP, GRAPH_WIDTH);
drawClusterBox(group, GRAPH_WIDTH, CLUSTER_HEIGHT);
drawCommitAmountCluster(group, GRAPH_WIDTH, CLUSTER_HEIGHT);
drawSubGraph(svgRef, data, detailElementHeight);
drawTotalLine(svgRef, data, detailElementHeight, SVG_MARGIN, CLUSTER_HEIGHT, NODE_GAP, GRAPH_WIDTH);
};

export const useHandleClusterGraph = ({
data,
clusterSizes,
selectedIndex,
setSelectedData,
}: {
clusterSizes: number[];
selectedIndex: number[];
data: ClusterNode[];
setSelectedData: Dispatch<React.SetStateAction<ClusterNode[]>>;
}) => {
const svgRef = useRef<SVGSVGElement>(null);
const prevSelected = useRef<number[]>([-1]);

const clusterGraphElements = data.map((cluster, i) => ({
const clusterGraphElements = data.map((cluster) => ({
cluster,
clusterSize: clusterSizes[i],
clusterSize: cluster.commitNodeList.length,
selected: {
prev: prevSelected.current,
current: selectedIndex,
},
}));

const handleClickCluster = useCallback(
(_: PointerEvent, d: ClusterGraphElement) =>
setSelectedData(selectedDataUpdater(d.cluster, d.cluster.commitNodeList[0].clusterId)),
(_: PointerEvent, d: ClusterGraphElement) => {
const targetIndex = d.cluster.commitNodeList[0].clusterId;
setSelectedData(selectedDataUpdater(d.cluster, targetIndex));
},
[setSelectedData]
);
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@
stroke: var(--primary-color);
stroke-width: 1;
}

.circle-group {
fill: var(--primary-color);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,13 @@ const ClusterGraph = () => {

const svgRef = useHandleClusterGraph({
data,
clusterSizes,
selectedIndex,
setSelectedData,
});

console.log(styles);

return (
<svg
className={cx("cluster-graph")}
className={cx("cluster-graph__container", "cluster-graph__total-line", "circle-group")}
ref={svgRef}
width={SVG_WIDTH}
height={graphHeight}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@ export function getGraphHeight(clusterSizes: number[]) {
return clusterSizes.length * CLUSTER_HEIGHT + clusterSizes.length * NODE_GAP + NODE_GAP;
}

export function getStartYEndY(d: ClusterGraphElement, a: number, detailElementHeight: number) {
const selected = d.selected.current;
const selectedLength = selected.filter((selectedIdx) => selectedIdx < a).length;
const selectedLongerHeight = selectedLength * detailElementHeight;
const startY = SVG_MARGIN.top + 20 + (a + 1) * (CLUSTER_HEIGHT + NODE_GAP) + selectedLongerHeight;
const endY = startY + detailElementHeight - 50;
return { startY, endY };
}

export function getTranslateAfterSelect(
d: ClusterGraphElement,
i: number,
detailElementHeight: number,
isPrev = false
isPrev = false // TODO - this param can be removed
) {
const selected = isPrev ? d.selected.prev : d.selected.current;
const selectedLength = selected.filter((selectedIdx) => selectedIdx < i).length;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as d3 from "d3";
import type { RefObject } from "react";

import type { ClusterGraphElement } from "../ClusterGraph.type";
import { getStartYEndY } from "../ClusterGraph.util";
import { GRAPH_WIDTH } from "../ClusterGraph.const";

// create tootip (HTML)
const tooltip = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("Tooltip");

const calculateCirclePositions = (numOfCircles: number, startY: number, endY: number, gap: number) => {
const positionStrategies = new Map<number, (start: number, end: number) => number[]>([
[1, (start, end) => [(start + end) / 2]],
[2, (start, end) => [(3 * start + end) / 4, (start + 3 * end) / 4]],
]);

const strategy = positionStrategies.get(numOfCircles);

return strategy ? strategy(startY, endY) : Array.from({ length: numOfCircles }, (_, i) => startY + i * gap);
};

export const drawSubGraph = (
svgRef: RefObject<SVGSVGElement>,
data: ClusterGraphElement[],
detailElementHeight: number
) => {
const allCirclePositions = data.reduce(
(acc, clusterData, index) => {
if (clusterData.selected.current.includes(index)) {
const { startY, endY } = getStartYEndY(clusterData, index, detailElementHeight);
const numOfCircles = clusterData.cluster.commitNodeList.length;
const gap = (endY - startY) / (numOfCircles - 1);
const circlePositions = calculateCirclePositions(numOfCircles, startY, endY, gap);

const enrichedPositions = circlePositions.map((y, circleIndex) => ({ y, clusterData, circleIndex }));
return acc.concat(enrichedPositions);
}
return acc;
},
[] as Array<{ y: number; clusterData: ClusterGraphElement; circleIndex: number }>
);

const circleRadius = 5;

d3.select(svgRef.current)
.selectAll(".circle-group")
.data(allCirclePositions)
.join("circle")
.attr("class", "circle-group")
.attr("cx", GRAPH_WIDTH / 2 + 2)
.attr("cy", (d) => d.y)
.attr("r", circleRadius)
.on("mouseover", (_, { clusterData, circleIndex }) => {
const { commitNodeList } = clusterData.cluster;
const targetIndex = commitNodeList.length - 1 - circleIndex;
const info = commitNodeList[targetIndex].commit.message;
tooltip.text(info);
return tooltip.style("visibility", "visible");
})
.on("mousemove", (event) => {
return tooltip.style("top", `${event.pageY - 10}px`).style("left", `${event.pageX + 10}px`);
})
.on("mouseout", () => {
return tooltip.style("visibility", "hidden");
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ export const drawTotalLine = (
.attr("x2", svgMargin.left + graphWidth / 2)
.attr("y2", (d) => d.end + d.selected.prev.length * detailElementHeight)
.transition()
.attr("y2", (d) => d.end + d.selected.current.length * detailElementHeight);
.attr("y2", (d) => d.end + d.selected.current.length * detailElementHeight)
.attr("pointer-events", "none");
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./drawClusterBox";
export * from "./drawCommitAmountCluster";
export * from "./drawTotalLine";
export * from "./destroyClusterGraph";
export * from "./drawSubGraph";
33 changes: 33 additions & 0 deletions packages/view/tests/home.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
import { test, expect } from "@playwright/test";

test.describe("home", () => {
const CLICK_INDEX = 0;
test.beforeEach(async ({ page }) => {
await page.goto("/");
});

test("has title", async ({ page }) => {
await expect(page).toHaveTitle(/Githru/);
});

test("when click cluster", async ({ page }) => {
await page.waitForSelector("[data-testid=cluster-graph__container]", { state: "attached" });

const childContainers = await page.$$("[data-testid=cluster-graph__container]");

if (childContainers.length > CLICK_INDEX) {
await childContainers[CLICK_INDEX].scrollIntoViewIfNeeded();
await childContainers[CLICK_INDEX].click();
} else {
throw new Error("No child containers found");
}

// waiting for changing
await page.waitForTimeout(1000);

const newChildContainers = await page.$$("[data-testid=cluster-graph__container]");

const targetIndexForCheck = CLICK_INDEX + 1;
const transformPositionForCheck = 10 + targetIndexForCheck * 50 + 220;
if (newChildContainers.length > targetIndexForCheck) {
const transformValue = await newChildContainers[targetIndexForCheck].getAttribute("transform");

if (transformValue !== null) {
expect(transformValue).toBe(`translate(2, ${transformPositionForCheck})`);
} else {
throw new Error("Transform attribute not found");
}
} else {
throw new Error("Not enough child containers found");
}
});
});
4 changes: 3 additions & 1 deletion packages/view/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
} /* Specify a set of entries that re-map imports to additional lookup locations. */,
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
"types": [
"@playwright/test"
] /* Specify type package names to be included without being referenced in a source file. */,
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
"resolveJsonModule": true /* Enable importing .json files. */,
Expand Down

0 comments on commit 301cfeb

Please sign in to comment.