Skip to content

Commit 67d00d9

Browse files
authored
Merge pull request #20 from oslabs-beta/feature/garrett
Feature/garrett
2 parents d5f3d1e + d7c817b commit 67d00d9

21 files changed

+467
-445
lines changed

src/app/components/StateRoute/ComponentMap/ComponentMap.tsx

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,17 @@ export default function ComponentMap({
116116
const tooltipStyles: ToolTipStyles = {
117117
...defaultStyles,
118118
minWidth: 60,
119-
maxWidth: 300,
119+
maxWidth: 250,
120120
lineHeight: '18px',
121-
zIndex: 100,
122121
pointerEvents: 'all !important',
122+
margin: 0,
123+
padding: 0,
124+
borderRadius: '8px',
123125
};
124126

125127
const scrollStyle: {} = {
126128
minWidth: '60',
127-
maxWidth: '300',
129+
maxWidth: '250',
128130
minHeight: '20px',
129131
maxHeight: '200px',
130132
overflowY: 'scroll',
@@ -325,16 +327,60 @@ export default function ComponentMap({
325327
})}
326328

327329
{tree.descendants().map((node, key) => {
328-
const widthFunc: number = (name) => {
329-
//returns a number that is related to the length of the name. Used for determining the node width.
330-
const nodeLength = name.length;
331-
// return nodeLength * 7 + 20; //uncomment this line if we want each node to be directly proportional to the name.length (instead of nodes of similar sizes to snap to the same width)
330+
const calculateNodeWidth = (text: string): number => {
331+
const nodeLength = text.length;
332332
if (nodeLength <= 5) return nodeLength + 50;
333333
if (nodeLength <= 10) return nodeLength + 120;
334334
return nodeLength + 140;
335335
};
336336

337-
const width: number = widthFunc(node.data.name); // the width is determined by the length of the node.name
337+
// Find the maximum width for any node
338+
const findMaxNodeWidth = (nodeData: any): number => {
339+
// If no children, return current node width
340+
if (!nodeData.children) {
341+
return calculateNodeWidth(nodeData.name);
342+
}
343+
344+
// Get width of current node
345+
const currentWidth = calculateNodeWidth(nodeData.name);
346+
347+
// Get max width from children
348+
const childrenWidths = nodeData.children.map((child) =>
349+
findMaxNodeWidth(child),
350+
);
351+
352+
// Return the maximum width found
353+
return Math.max(currentWidth, ...childrenWidths);
354+
};
355+
356+
// Truncate text for nodes that exceed a certain length
357+
const truncateText = (text: string, width: number, maxWidth: number): string => {
358+
// Calculate approximate text width
359+
const estimatedTextWidth = text.length * 8;
360+
361+
// If this node's width is close to the max width (within 10%), truncate it
362+
if (width >= maxWidth * 0.9) {
363+
const maxChars = Math.floor((width - 30) / 8); // -30 for padding + ellipsis
364+
return `${text.slice(0, maxChars)}...`;
365+
}
366+
367+
return text;
368+
};
369+
370+
const getNodeDimensions = (
371+
name: string,
372+
rootNode: any,
373+
): { width: number; displayText: string } => {
374+
const width = calculateNodeWidth(name);
375+
const maxWidth = findMaxNodeWidth(rootNode);
376+
const displayText = truncateText(name, width, maxWidth);
377+
378+
return { width, displayText };
379+
};
380+
381+
// Usage in your render function:
382+
const { width, displayText } = getNodeDimensions(node.data.name, startNode);
383+
338384
const height: number = 35;
339385
let top: number;
340386
let left: number;
@@ -432,9 +478,13 @@ export default function ComponentMap({
432478
return (
433479
<Group top={top} left={left} key={key} className='rect'>
434480
{node.depth === 0 && (
435-
<circle
481+
<rect
436482
className='compMapRoot'
437-
r={25}
483+
height={height}
484+
width={width}
485+
y={-height / 2}
486+
x={-width / 2}
487+
rx={10}
438488
onClick={() => {
439489
dispatch(toggleExpanded(node.data));
440490
hideTooltip();
@@ -451,7 +501,7 @@ export default function ComponentMap({
451501
width={width}
452502
y={-height / 2}
453503
x={-width / 2}
454-
rx={node.children ? 4 : 10}
504+
rx={10}
455505
onClick={() => {
456506
dispatch(toggleExpanded(node.data));
457507
hideTooltip();
@@ -490,7 +540,7 @@ export default function ComponentMap({
490540
textAnchor='middle'
491541
style={{ pointerEvents: 'none' }}
492542
>
493-
{node.data.name}
543+
{displayText}
494544
</text>
495545
</Group>
496546
);

src/app/components/StateRoute/ComponentMap/LinkControls.tsx

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,49 @@ const LinkControls = ({
2121
return nodeList;
2222
};
2323

24-
const nodeList = collectNodes(snapShots);
24+
const shouldIncludeNode = (node) => {
25+
// Return false if node has any context properties
26+
if (node?.componentData?.context && Object.keys(node.componentData.context).length > 0) {
27+
return false;
28+
}
29+
// Return false if node name ends with 'Provider'
30+
if (node?.name && node.name.endsWith('Provider')) {
31+
return false;
32+
}
33+
return true;
34+
};
35+
36+
const processTreeData = (node) => {
37+
if (!node) return null;
38+
39+
// Create a new node
40+
const newNode = { ...node };
41+
42+
if (node.children) {
43+
// Process all children first
44+
const processedChildren = node.children
45+
.map((child) => processTreeData(child))
46+
.filter(Boolean); // Remove null results
47+
48+
// For each child that shouldn't be included, replace it with its children
49+
newNode.children = processedChildren.reduce((acc, child) => {
50+
if (shouldIncludeNode(child)) {
51+
// If child should be included, add it directly
52+
acc.push(child);
53+
} else {
54+
// If child should be filtered out, add its children instead
55+
if (child.children) {
56+
acc.push(...child.children);
57+
}
58+
}
59+
return acc;
60+
}, []);
61+
}
62+
63+
return newNode;
64+
};
65+
const filtered = processTreeData(snapShots);
66+
const nodeList = collectNodes(filtered);
2567

2668
return (
2769
<div className='link-controls'>

src/app/components/StateRoute/ComponentMap/ToolTipDataDisplay.tsx

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,46 @@ const ToolTipDataDisplay = ({ data }) => {
88
scheme: 'custom',
99
base00: 'transparent',
1010
base0B: '#1f2937', // dark navy for strings
11-
base0D: '#60a5fa', // Blue for keys
12-
base09: '#f59e0b', // Orange for numbers
13-
base0C: '#EF4444', // red for nulls
11+
base0D: '#60a5fa', // Keys
12+
base09: '#f59e0b', // Numbers
13+
base0C: '#EF4444', // Null values
14+
};
15+
16+
// Helper function to parse stringified JSON in object values
17+
const parseStringifiedValues = (obj) => {
18+
if (!obj || typeof obj !== 'object') return obj;
19+
20+
const parsed = { ...obj };
21+
for (const key in parsed) {
22+
if (typeof parsed[key] === 'string') {
23+
try {
24+
// Check if the string looks like JSON
25+
if (parsed[key].startsWith('{') || parsed[key].startsWith('[')) {
26+
const parsedValue = JSON.parse(parsed[key]);
27+
parsed[key] = parsedValue;
28+
}
29+
} catch (e) {
30+
// If parsing fails, keep original value
31+
continue;
32+
}
33+
} else if (typeof parsed[key] === 'object') {
34+
parsed[key] = parseStringifiedValues(parsed[key]);
35+
}
36+
}
37+
return parsed;
1438
};
1539

1640
const formatReducerData = (reducerStates) => {
41+
// Check if reducerStates exists and is an array
42+
if (!Array.isArray(reducerStates)) {
43+
return {};
44+
}
45+
1746
return reducerStates.reduce((acc, reducer) => {
18-
acc[reducer.hookName || 'Reducer'] = reducer.state;
47+
// Add additional type checking for reducer object
48+
if (reducer && typeof reducer === 'object') {
49+
acc[reducer.hookName || 'Reducer'] = reducer.state;
50+
}
1951
return acc;
2052
}, {});
2153
};
@@ -29,8 +61,18 @@ const ToolTipDataDisplay = ({ data }) => {
2961
return null;
3062
}
3163

32-
if (isReducer) {
33-
const formattedData = formatReducerData(content);
64+
// Parse any stringified JSON before displaying
65+
const parsedContent = parseStringifiedValues(content);
66+
67+
if (isReducer && parsedContent) {
68+
// Only try to format if we have valid content
69+
const formattedData = formatReducerData(parsedContent);
70+
71+
// Check if we have any formatted data to display
72+
if (Object.keys(formattedData).length === 0) {
73+
return null;
74+
}
75+
3476
return (
3577
<div className='tooltip-section'>
3678
{Object.entries(formattedData).map(([hookName, state]) => (
@@ -49,17 +91,17 @@ const ToolTipDataDisplay = ({ data }) => {
4991
<div className='tooltip-section'>
5092
<div className='tooltip-section-title'>{title}</div>
5193
<div className='tooltip-data'>
52-
<JSONTree data={content} theme={jsonTheme} hideRoot shouldExpandNode={() => true} />
94+
<JSONTree data={parsedContent} theme={jsonTheme} hideRoot shouldExpandNode={() => true} />
5395
</div>
5496
</div>
5597
);
5698
};
5799

58100
return (
59101
<div className='tooltip-container'>
60-
{renderSection('Props', data.componentData.props)}
61-
{renderSection('State', data.componentData.state || data.componentData.hooksState)}
62-
{renderSection(null, data.componentData.reducerStates, true)}
102+
{renderSection('Props', data.componentData?.props)}
103+
{renderSection('State', data.componentData?.state || data.componentData?.hooksState)}
104+
{renderSection(null, data.componentData?.reducerStates, true)}
63105
</div>
64106
);
65107
};

0 commit comments

Comments
 (0)