
Progress Update 5
Update on the Dynamic Tree
A Major Change
In Progress Update 4, my tree could dynamically generate given a list of paths in this format: ['a/b/c/d', 'a/b/c/e', 'a/b/f']
However, our data is actually stored in the following format:
path : child|child|...|child
After verifying that dynamically generating a tree from a list of paths was viable, my next step was to implement the child layer child|child|...|child in my tree.
Since I already had a function generateTree(paths)that could generate a tree given an array of paths in the form a/b/c/d/e,
I thought that it would be the easiest if I simply added another function that took a path in the new format, ie a/b/c/d:e|f
and expanded it into paths in the original format: [a/b/c/d/e, a/b/c/d/f].
function getPathsFromPathWithChildren(pathWithChildren) {
const [path, children] = pathWithChildren.split(':');
if (!children) {
return [path];
}
const childPaths = children.split('|').map((child) => `${path}/${child}`);
return [path, ...childPaths];
}
Styling Struggles
A big source of my struggles stems from the tree design; some of the meshpaths can be really lengthy, and the resulting tree, when fully expanded, could be monstrously huge.
Because of this, I really wanted to include the ability to expand and collapse branches of the tree.
But how???
In exploring the MUI API documentation for the TreeView component, I found this tree component that gives you the option to expand the entire tree at once, then collapse individual branches.


So I did just that, with a few alternations.
One of the alterations I had to make was in implementing the EXPAND ALL functionality.
The API's example code handles it in this way:
const handleExpandClick = () => {
setExpanded((oldExpanded) =>
oldExpanded.length === 0 ? ['1', '5', '6', '7'] : [],
);
};
As you can see, they hardcode in the nodeIds of the nodes that have children, so that they are expanded with the EXPAND ALL button is clicked.
Obviously, because I'm dynamically generating a tree, I can't do that.
Thus, I wanted for my function to collect a list of the nodes with children as my tree was generated. In my function generateTree(paths), I have a nested recursive functionfindPathsWithChildren(node, path) that does that.
function findPathsWithChildren(node, path) {
if (Object.keys(node).length) {
if (path !== '') {
console.log(path);
pathsWithChildren.push(path);
}
Object.keys(node).forEach((key) => {
findPathsWithChildren(node[key], `${path}/${key}`);
});
}
}
That worked!...until I ran until another problem while testing.
Different branches of the tree could share a common condition along the path. For instance: Urogenital Diseases/Male Urogenital Diseases/Urogenital Abnormalities/... and Urogenital Diseases/Urogenital Abnormalities/...
This was a problem because, while dynamically rendering the tree, I was taking the easy route and setting the nodeIds equal to the condition name. That meant that whenever a user would collapse a branch under a condition, say Urogential Abnormalities, all other branches under branches containing Urogential Abnormalities would collapse as well.
Thankfully, it wasn't too difficult of a fix, as I just had to add a couple extra lines of code to set the nodeIds equal to the path up to that point, which are guaranteed to be unique in that tree.
const nodeId = parentKey ? `${parentKey}/${key}` : `/${key}`; // concatenate parent key and current key if parent key exists
Next, I wanted to highlight the nodes that were directly related to the trial. By that I mean, if the paths are [a/b/c/d:e|f, h/i/j:k], then the direct parents would be d and j.
So again, I edited my generateTree function so that it aggregated a list of all the nodes that appeared at the end of the path but before the :.
paths.forEach((path) => {
let start = path.lastIndexOf('/') + 1;
let end = path.indexOf(':');
highlightedNodes.push(path.substring(start, end));
/*
code block
*/
}