-
Notifications
You must be signed in to change notification settings - Fork 23
Custom useTabHook for Tab Navigation #621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom useTabHook for Tab Navigation #621
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @mshriver - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider creating a custom hook to encapsulate the tab navigation logic, as suggested in the description.
- It might be good to validate the tabIndex value in
handleHashChange
against the available tabs to prevent unexpected behavior.
Here's what I looked at during the review
- 🟡 General issues: 1 issue found
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
a505590
to
01f3ee5
Compare
@sourcery-ai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @mshriver - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider adding a comment to explain the purpose and usage of the
useTabHook
. - It looks like you're passing a hardcoded list of tab names to
useTabHook
; consider generating this list dynamically.
Here's what I looked at during the review
- 🟢 General issues: all looks good
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟡 Complexity: 1 issue found
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
36f1632
to
2df9454
Compare
On Run's details page, opening any of the test results in Results Tree tab bugs the tab selection. |
1bc8e1f
to
4707f68
Compare
@hugohezel thanks! Yep, the nested tabs can't also be tracked in the URL. Thought about slicing out multiple hash references, but there's no real need or use to also include the selected result tab in the URL as well. The selected result and tree expansion can't be reached by link alone. Classify Failures and Results Tree tabs both end up needing the |
4707f68
to
9af6468
Compare
Using WCA@IBM! Add default tab hashes to result paths trying to update the hash after navigation via effect within ResultView ends up making the history ugly regardless of use of navigation hooks on tab selection. Just set the hash on the initial target for summary Create custom useTabHook Move buildTree Convert Run to useTabHook Use #summary hash for run hrefs
When nested, the activeTab should not sync with the location hash. add useTabHook to jenkinsjob analysis Update link composition in filterheatmap provide hash for tabbed views use `${}` syntax Update useTabHook navigation with location include search and path as-is, search is used in a few spots
9af6468
to
6fef9d1
Compare
e1fc316
to
fc9333f
Compare
@sourcery-ai review |
Reviewer's Guide by SourceryThis pull request introduces a custom No diagrams generated as the changes look simple and do not need a visual representation. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @mshriver - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider extracting the tree building logic into a separate module for better organization and reusability.
- The new
useTabHook
looks good, but ensure it's thoroughly tested, especially the edge cases and fallback behavior.
Here's what I looked at during the review
- 🟢 General issues: all looks good
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟡 Complexity: 2 issues found
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
}, [artifactTabs]); | ||
|
||
// Tab state and navigation hooks/effects | ||
const {activeTab, onTabSelect} = useTabHook( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider merging the artifact key extraction into the existing useMemo hook to compute all tab keys at once, avoiding an extra useCallback wrapper function and simplifying the code.
You can simplify the extraction of the artifact keys by avoiding an extra useCallback
wrapper when you already have the artifact data. For example, merge the key extraction into a single useMemo
that computes all tab keys. This reduces indirection while keeping functionality intact:
const tabKeys = useMemo(() => {
// Creates an array of tab keys, including artifacts directly
return ['summary', 'testHistory', 'testObject', ...artifacts.map(art => art.filename)];
}, [artifacts]);
const { activeTab, onTabSelect } = useTabHook(tabKeys, defaultTab, skipHash);
This change removes the extra artifactKeys
callback and makes the key computation clear and directly dependent on artifacts
.
@@ -151,6 +153,91 @@ export function buildBadge (key, value, isRead, onClick) { | |||
} | |||
} | |||
|
|||
export const buildResultsTree = (treeResults) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider refactoring the buildResultsTree function to extract helper functions for creating child nodes and selecting icons to reduce nesting and improve readability and maintainability of the code.
Consider refactoring the deep nested logic inside buildResultsTree
by extracting helper functions. For example, you can create one helper to get or create a child node and another to select the icon based on the test result. This will simplify the main loop and reduce cognitive load.
Before:
treeResults.forEach(testResult => {
const pathParts = processPyTestPath(cleanPath(testResult.metadata.fspath));
let children = treeStructure;
pathParts.forEach(dirName => {
let child = children.find(item => item.name == dirName);
if (!child) {
child = {
name: dirName,
id: dirName,
children: [],
hasBadge: true,
_stats: { count: 0, passed: 0, failed: 0, skipped: 0, error: 0, xpassed: 0, xfailed: 0 }
};
if (dirName.endsWith('.py')) {
child.icon = <FileIcon />;
child.expandedIcon = <FileIcon />;
}
children.push(child);
}
// update stats and badge...
child._stats[testResult.result] += 1;
child._stats.count += 1;
const passPercent = getPassPercent(child._stats);
const className = getBadgeClass(passPercent);
child.customBadgeContent = `${passPercent}%`;
child.badgeProps = { className: className };
children = child.children;
});
let icon = <QuestionCircleIcon />;
if (testResult.result === 'passed') { icon = <CheckCircleIcon />; }
else if (testResult.result === 'failed') { icon = <TimesCircleIcon />; }
// ... other conditions
children.push({
id: testResult.id,
name: testResult.test_id,
icon: <span className={testResult.result}>{icon}</span>,
_testResult: testResult
});
});
After (suggested changes):
- Extract a helper to get or create a child node:
function getOrCreateChild(children, name) {
let child = children.find(item => item.name === name);
if (!child) {
child = {
name,
id: name,
children: [],
hasBadge: true,
_stats: { count: 0, passed: 0, failed: 0, skipped: 0, error: 0, xpassed: 0, xfailed: 0 }
};
if (name.endsWith('.py')) {
child.icon = <FileIcon />;
child.expandedIcon = <FileIcon />;
}
children.push(child);
}
return child;
}
- Extract icon selection:
function selectIcon(result) {
switch (result) {
case 'passed': return <CheckCircleIcon />;
case 'failed': return <TimesCircleIcon />;
case 'error': return <ExclamationCircleIcon />;
case 'skipped': return <ChevronRightIcon />;
case 'xfailed': return <CheckCircleIcon />;
case 'xpassed': return <TimesCircleIcon />;
default: return <QuestionCircleIcon />;
}
}
- Refactor
buildResultsTree
:
export const buildResultsTree = (treeResults) => {
const getPassPercent = (stats) => {
return stats.count > 0
? Math.round(((stats.passed + stats.xfailed) / stats.count) * 100)
: 'N/A';
};
const getBadgeClass = (passPercent) => {
if (passPercent > 90) return 'passed';
if (passPercent > 75) return 'error';
return 'failed';
};
let treeStructure = [];
treeResults.forEach(testResult => {
const pathParts = processPyTestPath(cleanPath(testResult.metadata.fspath));
let children = treeStructure;
pathParts.forEach(dirName => {
const child = getOrCreateChild(children, dirName);
// Update stats and badge
child._stats[testResult.result] += 1;
child._stats.count += 1;
const passPercent = getPassPercent(child._stats);
child.customBadgeContent = `${passPercent}%`;
child.badgeProps = { className: getBadgeClass(passPercent) };
children = child.children;
});
// Add leaf node with appropriate icon
children.push({
id: testResult.id,
name: testResult.test_id,
icon: <span className={testResult.result}>{selectIcon(testResult.result)}</span>,
_testResult: testResult
});
});
return treeStructure;
};
These steps keep functionality intact while reducing nesting and making each piece easier to test and maintain.
* Updates for tab history handling Using WCA@IBM! Add default tab hashes to result paths trying to update the hash after navigation via effect within ResultView ends up making the history ugly regardless of use of navigation hooks on tab selection. Just set the hash on the initial target for summary Create custom useTabHook Move buildTree Convert Run to useTabHook Use #summary hash for run hrefs * useTabHook, restore state and add skipHash When nested, the activeTab should not sync with the location hash. add useTabHook to jenkinsjob analysis Update link composition in filterheatmap provide hash for tabbed views use `${}` syntax Update useTabHook navigation with location include search and path as-is, search is used in a few spots * Catch error on project request from header * useTabHook in jenkinsjobanalysis view
* Updates for tab history handling Using WCA@IBM! Add default tab hashes to result paths trying to update the hash after navigation via effect within ResultView ends up making the history ugly regardless of use of navigation hooks on tab selection. Just set the hash on the initial target for summary Create custom useTabHook Move buildTree Convert Run to useTabHook Use #summary hash for run hrefs * useTabHook, restore state and add skipHash When nested, the activeTab should not sync with the location hash. add useTabHook to jenkinsjob analysis Update link composition in filterheatmap provide hash for tabbed views use `${}` syntax Update useTabHook navigation with location include search and path as-is, search is used in a few spots * Catch error on project request from header * useTabHook in jenkinsjobanalysis view
The browser back/forward and direct tab navigation via hash is not working correctly in the feature branch.
patternfly/patternfly-react#11715
The PF demo for this isn't functional.
This introduces a custom hook for tab state and navigation via location hash. I've integrated on the Run and Result views.
I've also made some small updates across the
Link
hrefs to use the default tab location hash on initial navigation, which makes for a better experience than just updating it in effects.Summary by Sourcery
Introduce a custom useTabHook for consistent tab navigation and hash-based routing across multiple components
New Features:
Enhancements:
Chores: