Skip to content

feat!: externalize sidebar toggle and remove navOpen state from the SDK#3088

Open
oliverlaz wants to merge 8 commits intomasterfrom
fix/hide-toggle-sidebar-without-channel-list
Open

feat!: externalize sidebar toggle and remove navOpen state from the SDK#3088
oliverlaz wants to merge 8 commits intomasterfrom
fix/hide-toggle-sidebar-without-channel-list

Conversation

@oliverlaz
Copy link
Copy Markdown
Member

@oliverlaz oliverlaz commented Apr 2, 2026

🎯 Goal

The SDK currently couples itself to a specific layout pattern (collapsible sidebar) by owning navOpen state in ChatContext. This creates problems — e.g., the sidebar toggle renders even when no sidebar exists — and prevents apps from fully controlling their own layout.

This PR removes all sidebar state management from the SDK and makes the app fully responsible for sidebar visibility.

🛠 Implementation details

Sidebar toggle externalized (commit 1-3):

  • Removed ToggleSidebarButton component from the SDK
  • Added SidebarToggle slot to ComponentContext — apps provide their own toggle via WithComponents
  • All 4 headers (ChannelHeader, ChannelListHeader, ThreadHeader, ThreadListHeader) render the slot when provided
  • Removed IconSidebar (moved to vite example), MenuIcon prop, ToggleButtonIcon prop

navOpen state removed (commit 4):

  • Removed navOpen, closeMobileNav, openMobileNav from ChatContextValue
  • Removed initialNavOpen from ChatProps
  • Removed NAV_SIDEBAR_DESKTOP_BREAKPOINT constant, useMobileNavigation hook, useIsMobileViewport hook
  • Removed auto-close-on-channel-selection (mobile) from setActiveChannel
  • Removed auto-close-on-thread-click from ThreadListItemUI
  • Removed openMobileNav calls from ChatViewSelector buttons
  • Removed all 7 navOpen-dependent CSS classes and their SCSS rules from 6 files

Vite example updated:

  • New SidebarContext (SidebarProvider + useSidebar) replaces SDK nav state
  • SidebarToggle uses app-level sidebar state
  • Layout panels read sidebarOpen from app context
  • Resize handle toggles app-level sidebar state
  • CSS rules hide the expand toggle when sidebar is visible (mutual exclusivity)

🎨 UI Changes

No visual changes in the vite example — sidebar toggle behavior is identical. Apps that don't provide a SidebarToggle will have no toggle button in any header.

⚠️ Breaking changes

Removed API Migration
ChatContextValue.navOpen Own your sidebar state
ChatContextValue.closeMobileNav / openMobileNav Own your toggle functions
ChatProps.initialNavOpen Move to own state initializer
NAV_SIDEBAR_DESKTOP_BREAKPOINT Define own constant
useMobileNavigation hook Implement own click-outside logic
ToggleSidebarButton component Provide SidebarToggle via WithComponents
MenuIcon / ToggleButtonIcon props Use SidebarToggle slot instead
7 navOpen CSS classes Apply own classes from own state

… rendered

The expand button in ChannelHeader is pointless when there is no
ChannelList in the component tree. Use the existing ChannelListContext
to detect whether a ChannelList is present and conditionally render
the button.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

Size Change: -3.28 kB (-0.53%)

Total Size: 610 kB

📦 View Changed
Filename Size Change
dist/cjs/emojis.js 2.96 kB +4 B (+0.14%)
dist/cjs/index.js 236 kB -1.18 kB (-0.5%)
dist/cjs/WithAudioPlayback.js 42.5 kB -123 B (-0.29%)
dist/css/index.css 44.9 kB -604 B (-1.33%)
dist/es/emojis.mjs 2.47 kB +2 B (+0.08%)
dist/es/index.mjs 234 kB -1.25 kB (-0.53%)
dist/es/WithAudioPlayback.mjs 42.4 kB -132 B (-0.31%)
ℹ️ View Unchanged
Filename Size
dist/cjs/audioProcessing.js 1.32 kB
dist/cjs/mp3-encoder.js 1.27 kB
dist/css/emoji-replacement.css 456 B
dist/es/audioProcessing.mjs 1.31 kB
dist/es/mp3-encoder.mjs 756 B

compressed-size-action

Verify the button is hidden without ChannelList and shown with it.
Update existing tests to provide ChannelListContext where needed.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

❌ Patch coverage is 85.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.58%. Comparing base (30e45fa) to head (594930d).

Files with missing lines Patch % Lines
src/components/ChatView/ChatView.tsx 60.00% 2 Missing ⚠️
...components/Threads/ThreadList/ThreadListItemUI.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3088   +/-   ##
=======================================
  Coverage   79.57%   79.58%           
=======================================
  Files         426      423    -3     
  Lines       12181    12107   -74     
  Branches     3914     3887   -27     
=======================================
- Hits         9693     9635   -58     
+ Misses       2488     2472   -16     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…nent

Remove ToggleSidebarButton from the SDK and replace it with a
SidebarToggle slot in ComponentContext. Apps provide their own toggle
component via WithComponents; the SDK renders it in the appropriate
header slots with conditional visibility:

- ChannelHeader/ThreadHeader: render when sidebar is collapsed
- ChannelListHeader: render when a channel is active
- ThreadListHeader: render when a thread is active

Also removes IconSidebar (moved to vite example), MenuIcon prop
from ChannelHeader/ThreadHeader, and ToggleButtonIcon prop from
ChannelListHeader/ThreadListHeader.
@oliverlaz oliverlaz changed the title fix(ChannelHeader): hide toggle sidebar button when no ChannelList is rendered feat: make sidebar toggle an externally-provided component via ComponentContext Apr 3, 2026
@oliverlaz oliverlaz changed the title feat: make sidebar toggle an externally-provided component via ComponentContext fix: make sidebar toggle an externally-provided component via ComponentContext Apr 3, 2026
BREAKING CHANGE: The SDK no longer manages sidebar visibility. The
following public APIs have been removed:

- `ChatContextValue.navOpen`, `closeMobileNav`, `openMobileNav`
- `ChatProps.initialNavOpen`
- `NAV_SIDEBAR_DESKTOP_BREAKPOINT` exported constant
- `useMobileNavigation` exported hook

Sidebar toggle is now fully app-owned. Apps provide a `SidebarToggle`
component via `WithComponents` and manage their own open/close state.

The vite example app demonstrates this with a new `SidebarContext`
that replaces the SDK's nav state.

Also removes all navOpen-dependent CSS classes and SCSS rules from
the SDK, and deletes `useMobileNavigation` and `useIsMobileViewport`
hooks.
@oliverlaz oliverlaz changed the title fix: make sidebar toggle an externally-provided component via ComponentContext feat!: externalize sidebar toggle and remove navOpen state from the SDK Apr 7, 2026
…Content

Split the single SidebarToggle slot into two generic slots:

- HeaderStartContent: rendered at the start of content headers
  (ChannelHeader, ThreadHeader)
- HeaderEndContent: rendered at the end of sidebar headers
  (ChannelListHeader, ThreadListHeader)

These are position-aware, generic slots that can serve any purpose
(sidebar toggle, breadcrumbs, back button, etc.).
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.

2 participants