Skip to content

ux(recommendations): cascading categorical filter distinct values (closes #164) - #822

Merged
cristim merged 3 commits into
mainfrom
fix/164-wave8
Jul 17, 2026
Merged

cristim merged 3 commits into
mainfrom
fix/164-wave8

Conversation

@cristim

@cristim cristim commented May 28, 2026 •

Copy link
Copy Markdown
Member

Summary

  • When building a categorical filter popover for column X, apply all OTHER active column filters first so the dropdown only shows values that produce non-empty results (Excel-style cascading filter).
  • Adds alwaysInclude pin: any value in the column's own active filter is always shown, so the user can change/deselect it without first clearing every other filter.
  • 2 new tests: service popover narrows to AWS services when Provider=AWS is active; provider popover still shows all providers when opening its own column (own-filter excluded from narrowing).

Test plan

  • cd frontend && npx tsc --noEmit -- clean
  • cd frontend && npx jest src/__tests__/recommendations*.test.ts -- 331 passed, 0 failed
  • Manual: set Provider=AWS, open Service popover -- only ec2/rds/elasticache etc. appear, no Azure services
  • Manual: set Provider=AWS, open Provider popover -- all providers appear (own filter excluded)
  • Manual: set Provider=AWS + Service=ec2, open Provider popover -- AWS still appears (alwaysInclude pin)

Closes #164

Summary by CodeRabbit

  • New Features
    • Column filter popovers now intelligently narrow available checkbox options based on other active filters (cross-column “cascading” behavior).
    • Values you’ve already selected remain visible and selectable even when they would otherwise be excluded by the narrowed results.
  • Bug Fixes
    • Improved safety in popover rendering to ensure hostile payloads are treated as plain text.
  • Tests
    • Added and updated coverage for cascading categorical distinct-value behavior across related columns and for always-include/pinned selections.

@cristim cristim added triaged Item has been triaged priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/eventually No deadline impact/many Affects most users effort/xs Trivial / one-liner type/feat New capability labels May 28, 2026
@coderabbitai

coderabbitai Bot commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 54 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 54e85b61-ded1-4919-a89f-8aad3dea481f

📥 Commits

Reviewing files that changed from the base of the PR and between 59fee93 and 4d043d8.

📒 Files selected for processing (2)
  • frontend/src/__tests__/recommendations.test.ts
  • frontend/src/recommendations.ts
📝 Walkthrough

Walkthrough

This PR makes categorical column-filter popovers cross-filter aware by computing distinct values from recommendations filtered by all other active columns' constraints, while preserving the currently-active column's own selections in the dropdown via an alwaysInclude set.

Changes

Categorical Cascading Filters

Layer / File(s) Summary
Distinct-values helper enhanced with always-include set
frontend/src/recommendations.ts
distinctValuesForColumn now accepts an optional alwaysInclude parameter and merges those values into the computed distinct set so they remain visible in the popover even when cross-filtering would hide them.
Popover content builder and column popover orchestration
frontend/src/recommendations.ts
buildPopoverContent signature is extended with alwaysInclude for its categorical branch. openColumnPopover now filters recommendations by all other active column filters (excluding the column being edited), computes alwaysInclude from that column's own active filter values, and passes both into buildPopoverContent for the merged distinct list.
Test suite for cascading categorical filters
frontend/src/__tests__/recommendations.test.ts
Updated the categorical popover distinct-values test to reflect filtering by the rec set when no other active filters apply. Added a new Issue #164 test suite verifying that the service popover narrows to AWS-only values when provider=aws is active, the provider popover lists all providers (excluding provider's own filter from cascading), and validates alwaysInclude behavior and XSS safety for hostile payloads.

Sequence Diagram

sequenceDiagram
  participant User
  participant openColumnPopover
  participant applyColumnFilters
  participant buildPopoverContent
  participant distinctValuesForColumn
  User->>openColumnPopover: Open column popover
  openColumnPopover->>applyColumnFilters: Filter recs by all OTHER column filters
  applyColumnFilters-->>openColumnPopover: Cross-filtered recommendation set
  openColumnPopover->>openColumnPopover: Compute alwaysInclude from column's own active set
  openColumnPopover->>buildPopoverContent: Pass filtered recs + alwaysInclude
  buildPopoverContent->>distinctValuesForColumn: Request distinct values with alwaysInclude
  distinctValuesForColumn-->>buildPopoverContent: Merged distinct set (filtered + always-include)
  buildPopoverContent-->>openColumnPopover: Popover content
  openColumnPopover-->>User: Rendered popover with cascaded options
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

  • LeanerCloud/CUDly#160: Introduces the per-column categorical filter popover feature that this PR extends with cross-column-aware distinct-value generation.
  • LeanerCloud/CUDly#181: Modifies the service-column popover's categorical distinct-value logic via tri-state toggle rendering that depends on the cross-filtered distinct values.

Poem

A popover grows wise, no longer blind,
It sees what filters others bind—
When AWS is set, show AWS alone,
Yet keep your choices carved in stone. 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately describes the main feature: cascading categorical filter distinct values as per Issue #164, concise and clear for history scanning.
Linked Issues check ✅ Passed All coding requirements from Issue #164 are met: cross-filtered distinct values, alwaysInclude mitigation, correct popover behavior, and empty-sentinel handling.
Out of Scope Changes check ✅ Passed All changes are in-scope: test additions for cascading filter behavior and implementation of cross-column-aware distinct values with alwaysInclude logic.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/164-wave8

Comment @coderabbitai help to get the list of available commands.

@cristim

cristim commented May 28, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 30, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@cristim

cristim commented Jun 3, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
frontend/src/__tests__/recommendations.test.ts (1)

2627-2680: ⚡ Quick win

Add a regression for the actual alwaysInclude case.

This suite proves cross-column narrowing and own-filter exclusion, but it still doesn’t pin the behavior this PR adds the alwaysInclude set for: an active value on the edited column should remain visible even when the other active filters would otherwise remove it from the distinct set. A contradictory case like provider=azure + service=ec2 would catch a regression here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/__tests__/recommendations.test.ts` around lines 2627 - 2680, Add
a regression test that verifies the alwaysInclude behavior: mock
state.getRecommendationsColumnFilters to include provider: { values: ['azure'] }
and service: { values: ['ec2'] }, call loadRecommendations(), open the service
column popover (use the same selector used in existing tests: 'th
.column-filter-btn[data-column="service"]' and '.column-filter-popover
.column-filter-item input[type="checkbox"]'), and assert that 'ec2' is still
present in the popover values (even though azure rows only have 'vm'); this uses
the existing helpers loadRecommendations and the mocked
state.getRecommendationsColumnFilters to ensure an active value on the edited
column (service) is always included.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@frontend/src/__tests__/recommendations.test.ts`:
- Around line 2627-2680: Add a regression test that verifies the alwaysInclude
behavior: mock state.getRecommendationsColumnFilters to include provider: {
values: ['azure'] } and service: { values: ['ec2'] }, call
loadRecommendations(), open the service column popover (use the same selector
used in existing tests: 'th .column-filter-btn[data-column="service"]' and
'.column-filter-popover .column-filter-item input[type="checkbox"]'), and assert
that 'ec2' is still present in the popover values (even though azure rows only
have 'vm'); this uses the existing helpers loadRecommendations and the mocked
state.getRecommendationsColumnFilters to ensure an active value on the edited
column (service) is always included.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5270fafa-a77e-4e2b-b524-75c12559a85b

📥 Commits

Reviewing files that changed from the base of the PR and between 4956d66 and e2c8ebe.

📒 Files selected for processing (2)
  • frontend/src/__tests__/recommendations.test.ts
  • frontend/src/recommendations.ts

@cristim

cristim commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@cristim have exceeded the limit for the number of chat messages per hour. Please wait 39 minutes and 13 seconds before sending another message.

@cristim

cristim commented Jun 6, 2026

Copy link
Copy Markdown
Member Author

Addressed CR pass-1 findings:

  • frontend/src/__tests__/recommendations.test.ts lines 2627-2680: add regression for alwaysInclude behavior in cascading filter popover -- fixed in 31eb7f1. Test uses a contradictory filter state (provider=azure + service=ec2) and asserts that the active service value (ec2) remains visible in the service popover even though cross-filtering by provider=azure would yield only vm. All 322 tests pass.

Note: CR re-ping skipped (org-level billing block active). Re-invoke /pr-iterate 822 after the org's CR billing is topped up to trigger a follow-up review pass.

@cristim

cristim commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@cristim
cristim changed the base branch from feat/multicloud-web-frontend to main June 9, 2026 15:45
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

When building a categorical filter popover for column X, apply all
OTHER active column filters to the rec set first. This means selecting
Provider=AWS before opening the Service popover now shows only AWS
services instead of the full cross-provider list, eliminating
ghost values that produce zero rows.

Mitigation for the "broaden a column" UX: any value that is part of
the column's own currently-active filter is always included in the
distinct list even if the cross-filtered set would omit it, so the
user can change or deselect the existing value without first clearing
every other filter.

Algorithm: full re-scan of the cross-filtered rec set on each popover
open via the existing applyColumnFilters path.

Tests: 2 new assertions -- service popover narrows to AWS-only services
when Provider=AWS is active; provider popover still shows all providers
when Provider=AWS is its own active filter (own-filter exclusion).
cristim added 2 commits July 17, 2026 19:27
…er popover

Add a contradictory-filter test (provider=azure + service=ec2) that
verifies values from a column's own active filter remain visible in the
popover even when cross-filtering by other columns would omit them.

This pins the alwaysInclude behavior added in the cascading-filter
implementation: without it, opening the service popover while
provider=azure is active would drop ec2 from the list entirely, making
it impossible to deselect without first clearing the provider filter.
…ilter popover

Add a regression test that verifies a service value containing an HTML
injection string (<script>alert(1)</script>) is stored in dataset and
rendered via textContent -- never interpreted as markup -- when the
cascading filter popover builds its checkbox list.
@cristim

cristim commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
cristim merged commit 7465a46 into main Jul 17, 2026
20 checks passed
@cristim
cristim deleted the fix/164-wave8 branch July 27, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/xs Trivial / one-liner impact/many Affects most users priority/p3 Polish / idea / may never ship severity/low Minor harm triaged Item has been triaged type/feat New capability urgency/eventually No deadline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ux(recommendations): cross-column-aware distinct values for categorical popovers (Excel-style)

1 participant