Skip to content

Conversation

@CrazyCapislav
Copy link

What does this PR change?

  • Implements drilldown functionality for namespace and resource costs in the Allocation Report (fixes #2942)
  • Updates recharts from 2.15.3 to 3.4.1 (major version update)
  • Replaces ResponsiveContainer wrapper component with built-in responsive prop in all chart components to comply with recharts 3.x API changes
  • Adds filters support to AllocationService to enable filtering by namespace, controllerKind, controller, and pod
  • Adds missing lodash dependency that was required but not declared in package.json

Does this PR relate to any other PRs?

  • No.

How will this PR impact users?

  • Users can now click on namespace rows (and other aggregation levels) to drill down and see more detailed cost breakdown
  • This provides better cost visibility and analysis capabilities, similar to what's available in Kubecost
  • Improves user experience by allowing intuitive navigation through cost allocation data
  • Charts will continue to work responsively as before, now using the updated recharts 3.x API
  • No breaking changes for end users - existing functionality remains unchanged

Does this PR address any GitHub or Zendesk issues?

  • Closes #2942

How was this PR tested?

  • Built the project successfully (code compiles without errors)
  • Verified no linter errors in all modified files
  • Tested drilldown logic:
    • Added filters support to AllocationService.fetchAllocation
    • Implemented drilldown function with proper hierarchy navigation (namespace → controllerKind → controller → pod → container)
    • Made table rows clickable with visual feedback (hover effect)
    • Added logic to prevent drilldown on idle, unallocated, unmounted rows, and container level (last level)
    • Implemented filter reset logic when navigating back to higher aggregation levels
    • Updated all 5 chart components to use recharts 3.x API (replaced ResponsiveContainer with responsive prop)
  • Added mock data fallback for testing when backend is not available

Does this PR require changes to documentation?

  • No documentation changes required - this is a UI enhancement that follows existing patterns and maintains the same functionality.

Have you labeled this PR and its corresponding Issue as "next release" if it should be part of the next OpenCost release? If not, why not?

  • This feature addresses a user-requested enhancement (issue #2942) and should be considered for the next release. The maintainers can decide on the release timing.

- Add filters support to AllocationService.fetchAllocation
- Implement drilldown function in Allocations page
- Make table rows clickable for drilldown navigation
- Add visual feedback (hover effect) for clickable rows
- Support drilldown hierarchy: namespace -> controllerKind -> controller -> pod -> container
- Reset filters when navigating back to higher aggregation levels

Fixes #2942

Signed-off-by: Rahim <[email protected]>
- Update recharts from 2.15.3 to 3.4.1
- Replace ResponsiveContainer with responsive prop in all chart components
- Add lodash dependency
- Update 5 chart components to use recharts 3.x API

Signed-off-by: Rahim <[email protected]>
@netlify
Copy link

netlify bot commented Nov 12, 2025

Deploy Preview for opencost-ui ready!

Name Link
🔨 Latest commit 88ebf55
🔍 Latest deploy log https://app.netlify.com/projects/opencost-ui/deploys/6925f6093ddb920008c9c233
😎 Deploy Preview https://deploy-preview-131--opencost-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@CrazyCapislav CrazyCapislav force-pushed the feature/drilldown-namespace-2942 branch from e982cd8 to 11b0d02 Compare November 12, 2025 11:23
@nealormsbee
Copy link
Contributor

Hey @CrazyCapislav, thanks for the contribution! I'd like to look at the code a little more in-depth before approving, and am running out of time today. But nominally, this looks great!

Will aim to provide a thorough review tomorrow. Thanks again.

@nealormsbee nealormsbee self-requested a review November 12, 2025 22:07
@CrazyCapislav CrazyCapislav force-pushed the feature/drilldown-namespace-2942 branch from 8defee2 to de4b0d2 Compare November 13, 2025 20:59
@ameijer
Copy link
Member

ameijer commented Nov 13, 2025

@CrazyCapislav this is amazing, thanks for your contribution! Is there any way you can driver the filtering from the URL? not apparent what we have drilled into when we do it. WDYT?

@CrazyCapislav
Copy link
Author

@CrazyCapislav this is amazing, thanks for your contribution! Is there any way you can driver the filtering from the URL? not apparent what we have drilled into when we do it. WDYT?

Thanks for the feedback! I've implemented URL-driven filtering for the drilldown functionality.
What's been added:
Filters are now stored in the URL parameter filter (e.g., ?agg=controllerKind&filter=namespace:"my-namespace")
When you drill down, the filters are automatically added to the URL
The URL is shareable — anyone opening the link will see the same filtered view
Browser back/forward navigation works correctly with the filter state
Filters are validated and trimmed if they don't match the current aggregation level
This makes it clear what you've drilled into, and you can share specific drilldown states via URL.
I'd appreciate it if this PR could be reviewed and approved, as I need it for my student work. Thank you!

@CrazyCapislav CrazyCapislav force-pushed the feature/drilldown-namespace-2942 branch from 023443b to 7765020 Compare November 15, 2025 10:05
Copy link
Contributor

@nealormsbee nealormsbee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the long gap in review time! I've been on vacation but am back today. Left a few requests / suggestions in code annotations.

The only high-level UX thing that I think would be nice is to indicate the applied filters in the UI somehow. Maybe in a "breadcrumb" style below the report title, since the filters are constrained to working in a specific drilldown pattern.

So, for example, if you drilled into a namespace called "Foo", you'd see something like

All Results > Foo

just below the title. And All Results would be a link that takes you back to an unfiltered view, aggregated by namespace.

Just ideas for a nice UX. I would not block the PR on not including this. If you can regenerate the package-lock.json and make a couple of small logic simplifications (or explain why I'm wrong about them), then I'm good to approve!

Thanks.

};

return (
<ResponsiveContainer width="100%" height={height}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you point me at the place where Recharts talks about the change from using ResponsiveContainer to a responsive prop?

This is more for my curiosity than anything. The graphs still exhibit the "Responsive" behavior, I just still see ResponsiveContainer referenced in their docs here and no mention of the new prop in the migration guide, so I'm wondering what I'm missing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, you can check recharts v3.3.0 changes: "add the responsive prop to any chart along with a height and width as if you were using ResponsiveContainer. One less component to wrap things with. ResponsiveContainer will continue to work for the life of 3.x". I think using props would be preferable, but it can be returned as it was.

Comment on lines +183 to +185
const isContainer = aggregateBy === "container";
// Only allow drilldown if there's a next level and we're not at container level
const canDrilldown = !isIdle && !isUnallocated && !isUnmounted && !isContainer && hasNextLevel && drilldown;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need an explicit isContainer check? container won't have an entry in the drilldown hierarchy, so hasNextLevel implies !isContainer, yes?

Comment on lines +198 to +200
responsive
height={height}
width="100%"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Total nit: can these match indentation with the other component props?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: just create a pre-commit hook that runs Prettier against the codease.

Comment on lines +123 to +126
// Load filters from URL on initial load or when URL changes
// Also validate that filters match the current aggregateBy level
useEffect(() => {
const currentSearchParams = new URLSearchParams(routerLocation.search);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all makes sense logically, and it works. You can actually simplify it a bit, unless I'm missing something.

One of the neat things about using routerLocation is that re-renders will happen when it changes, so you don't actually need to track filters in a separate piece of state. You can get rid of [filters, setFilters] = useState([]); and all the calls to setFilters(). You can just use const filters = parseFiltersFromUrl(filterParam), and then have this useEffect() update the URL directly with navigate().

Comment on lines +3795 to +3800
"node_modules/@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is actually used anywhere nor added to package.json. Can you regenerate the lockfile when you push?


// Mock data for testing drilldown functionality when backend is not available
// Only used when REACT_APP_USE_MOCK_DATA=true is explicitly set
const getMockData = (aggregate, filters) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Can this function go in its own file alongside this one, so we keep test / mock utilities separate from core service logic?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants