Skip to content

feat(content-sidebar): focused state for threaded-replies in activity-feed-v2#4650

Closed
mrscobbler wants to merge 3 commits into
box:masterfrom
mrscobbler:feature/threaded-replies-focus
Closed

feat(content-sidebar): focused state for threaded-replies in activity-feed-v2#4650
mrscobbler wants to merge 3 commits into
box:masterfrom
mrscobbler:feature/threaded-replies-focus

Conversation

@mrscobbler

@mrscobbler mrscobbler commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a focused state to threaded-replies rows in the v2 activity-feed sidebar when activeFeedEntryId matches the row id.
  • Sidebar-scoped: no changes to @box/threaded-annotations or @box/activity-feed — overrides the vendor card's border + background only when the wrapper has .is-focused.
  • Wrapper uses display: contents by default so unfocused layout is byte-identical to today.

Test plan

  • [ x] Deep-link to a comment in the v2 sidebar — matching thread shows blue border + 4% blue tint, others unchanged
  • [ x] Switch between two deep-links — old highlight clears, new one applies
  • [ x] Confirm <li data-activity-id> is still emitted (scroll-to-id still works) — key now lives on the wrapper div
  • [x ] Verify legacy v1 activity feed and viewer-overlay surfaces are visually unchanged

Follow-up: scroll-block tuning ({ block: 'center' }) for deep-links depends on an @box/activity-feed API change tracked separately.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a focused state for the active activity feed entry, visually highlighting the corresponding comment/threaded annotation.
    • Updated rendering so each row receives and applies activeFeedEntryId to drive the selected-thread styling.
  • Bug Fixes
    • Improved thread emphasis styling for the focused state, including clearer border and blue-tinted background.
  • Tests
    • Added tests to verify the thread-row wrapper receives the is-focused class only when activeFeedEntryId matches (including the undefined case).

@mrscobbler
mrscobbler requested review from a team as code owners June 24, 2026 19:25
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

ActivityFeedV2 forwards activeFeedEntryId into FeedItemRow. FeedItemRow marks matching threaded entries as focused, wraps comment and annotation threads in containers, and the stylesheet adds the corresponding focus border and background.

Changes

Activity feed focus state

Layer / File(s) Summary
Prop wiring and focus class
src/elements/content-sidebar/activity-feed-v2/ActivityFeedV2.tsx, src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx
ActivityFeedV2 forwards activeFeedEntryId, and FeedItemRow accepts it, destructures it, and computes the focused wrapper class from the current item id.
Threaded row wrappers
src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx
The comment and annotation render paths wrap ActivityFeed.List.ThreadedAnnotation in containers that use the computed thread row class, and the key moves off ThreadedAnnotation.
Focused row styles
src/elements/content-sidebar/activity-feed-v2/ActivityFeedV2.scss
The stylesheet adds the threaded row focus rule that sets border and background colors for focused annotation descendants.
Focus state tests
src/elements/content-sidebar/activity-feed-v2/__tests__/FeedItemRow.test.tsx
The FeedItemRow test suite adds cases for the thread-row wrapper class and the conditional is-focused class for matching and non-matching active entry ids.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • box/box-ui-elements#4533 — Introduced the threaded annotation rendering that this PR now wraps with active-row focus state.

Suggested labels

ready-to-merge

Suggested reviewers

  • ahorowitz123
  • jmcbgaston
  • tjuanitas

Poem

A bunny hopped through rows of light,
One thread glowed softly, focused right.
A blue-tinted nook, a border neat,
Made comment burrows feel complete.
🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding a focused state for threaded replies in activity-feed-v2.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description includes a clear summary, test plan, and follow-up notes, and matches the repository's expected merge-related guidance.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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)
src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx (1)

155-155: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

key on the single root wrapper <div> is redundant.

React only uses key to reconcile siblings in a list. Since this <div> is the sole element returned by FeedItemRow, and the list key is already applied where FeedItemRow is rendered (ActivityFeedV2.tsx Line 363), the key={item.id} here has no effect. Harmless, but can be dropped for clarity.

Also applies to: 220-220

🤖 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 `@src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx` at line 155,
The single root wrapper div in FeedItemRow has a redundant key prop, since React
only uses keys among sibling list items and the parent render in ActivityFeedV2
already provides the list key. Remove key={item.id} from the root div in
FeedItemRow (and the similar duplicate occurrence in the same component) while
keeping the existing className and structure unchanged.
🤖 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 `@src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx`:
- Line 155: The single root wrapper div in FeedItemRow has a redundant key prop,
since React only uses keys among sibling list items and the parent render in
ActivityFeedV2 already provides the list key. Remove key={item.id} from the root
div in FeedItemRow (and the similar duplicate occurrence in the same component)
while keeping the existing className and structure unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 07e8e2aa-1785-4faf-afb0-08f7e8da4ce2

📥 Commits

Reviewing files that changed from the base of the PR and between c833428 and 07f50f4.

📒 Files selected for processing (3)
  • src/elements/content-sidebar/activity-feed-v2/ActivityFeedV2.scss
  • src/elements/content-sidebar/activity-feed-v2/ActivityFeedV2.tsx
  • src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx

Comment thread src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx Outdated
mickr
mickr previously approved these changes Jun 24, 2026
Comment thread src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx
Comment thread src/elements/content-sidebar/activity-feed-v2/ActivityFeedV2.scss Outdated
Comment thread src/elements/content-sidebar/activity-feed-v2/ActivityFeedV2.scss Outdated
Comment thread src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx
mrscobbler and others added 2 commits June 29, 2026 13:33
…-feed-v2

When a comment or annotation is deep-linked via activeFeedEntryId, paint
a focused chrome on the matching thread by recoloring the vendor card's
existing border + background. The wrapper around ThreadedAnnotation is
display: contents by default so layout is untouched for unfocused rows
and the vendor's gap/padding contract is preserved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…s focus

- Use Blueprint tokens (--bp-box-blue-80, --bp-box-blue-opacity-04) and drop
  the now-unused variables import
- Remove redundant key from the single-root threadRow wrapper divs
- Add focus-state tests covering the threadRow wrapper and is-focused toggling

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mrscobbler
mrscobbler force-pushed the feature/threaded-replies-focus branch from 0496e01 to 1f47211 Compare June 29, 2026 20:35
Comment on lines +51 to +54
&.is-focused [class*='threadedAnnotations'] {
border-color: var(--bp-box-blue-80);
background-color: var(--bp-box-blue-opacity-04);
}

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.

I think these styles should live in the shared feature, not the UI Element. That way the styles are located in one codebase instead of spread across repos and then other consumers like notes won't need to duplicate this.

The other issue is that the threadedAnnotations wildcard is fragile since it relies on an internal classname that is not guaranteed by the component.

you could have ActivityFeed.List.ThreadedAnnotation receive an isFocused prop that forces the style on and then still set it with item.id === activeFeedEntryId

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can't have an actual focus state on the threaded annotations component itself due to the internal components also requiring focus state (@jackiejou keep me honest here). If we added an isFocused state it could be misleading? Open to moving to this strategy though if we're ok with this treatment not actually being used for accessibility related focus state-like behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Did a little more analysis:

The threaded annotations component already owns real focus semantics (:focus-within, root outline). This styling in this PR is a passive selected highlight, not focus — isFocused would collide and could get wired to outlines/aria later.

Proposal: add isActive (or isSelected) to the shared component, docs as selection-only (border + tint, no outline), scoped :not(.annotations) so the inline drawing/region popup is untouched.

The shared component is used across two surfaces -- the sidebar + annotations in a note/document/video/image -- so we'll have to get specific with the css on when the active state is activated vs not.

@tjuanitas thoughts ?

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.

yeah an isSelected or isHighlighted prop in ActivityFeed.List.ThreadedAnnotation could work

something like this:

// ActivityFeedV2.tsx
filteredItems.map(item => (
  <FeedItemRow
    key={item.id}
    isSelected={activeFeedEntryId === item.id} // calculate the condition here instead of in FeedItemRow
    ...
  />

// FeedItemRow.tsx
<ActivityFeed.List.ThreadedAnnotation
  isSelected={isSelected} // passthrough prop
  ...
/>

// Handle the styles in ThreadedAnnotationsV2

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok -- we're taking that approach! Will close out this PR.

@kduncanhsu kduncanhsu closed this Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants