feat: make the Slack channel picker a type-to-filter combobox - #194
Merged
StephenTangCook merged 1 commit intoAug 17, 2026
Merged
Conversation
Both channel pickers — the send dialog's and the load dialog's recent-messages scope — were plain `<select>`s, which stop being usable once a workspace has more channels than fit on a screen. Replace them with a shared `ChannelCombobox`: a text input that filters the list as you type, over a listbox of the matches. It follows the WAI-ARIA combobox pattern with manual selection — focus stays in the input and the active row is pointed at with `aria-activedescendant`, so typing only filters and nothing is committed until Enter or a click. Arrow keys (wrapping), Home/End, Escape-to-dismiss (without closing the enclosing dialog), and Tab are all wired. The list is portalled through the existing Radix popover rather than laid out under the field: both dialogs put the picker inside a pane that clips its own overflow, so an in-flow dropdown would be cut off. It matches the field's width and flips above it on a short viewport. Channel names now render as `# name` rather than `#name`, via a single `channelLabel` helper shared by the picker, the edit badge, the update dialog, and the load preview, so the spelling can't drift between them. Tests: `test/setup.ts` gains ResizeObserver / DOMRect.fromRect stubs that jsdom lacks and Radix's popper needs; the suites that drove the old `<select>` now drive the combobox via a `pickChannel` helper.
Contributor
Cloudflare preview✅ Deployed |
StephenTangCook
deleted the
claude/slack-channel-picker-combobox-m5m1yr
branch
August 17, 2026 18:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces both
<select>channel pickers — the send dialog's, and the load dialog's recent-messages scope — with a sharedChannelCombobox: a text input that filters the channel list as you type, over a listbox of the matches. Channel names now render as# namerather than#name.Why
A
<select>stops being usable once a workspace has more channels than fit on a screen — there's no way to search, only to scroll. Typing to narrow the list is the interaction users already expect from Slack's own channel switcher.The space after the
#keeps the hash a marker rather than the first character of the name, so# release-notesstays legible at badge sizes.What changed
src/components/channel-combobox.tsx(new) — follows the WAI-ARIA combobox pattern with manual selection: focus stays in the input and the active row is pointed at witharia-activedescendant, so typing only ever filters and nothing is committed until Enter or a click.#and its spacing, so pasting# genback into the field still findsgeneral.The list is portalled through the existing Radix popover rather than laid out under the field — both dialogs put the picker inside a pane that clips its own overflow, so an in-flow dropdown would be cut off. It matches the field's width and flips above it on a short viewport.
src/lib/channel-label.ts(new) — onechannelLabel()helper, shared by the picker, the toolbar edit badge, the update dialog, and the load preview, so the# namespelling can't drift between them.test/setup.ts— stubsResizeObserverandDOMRect.fromRect, which jsdom doesn't implement and Radix's popper constructs on mount.Test plan
pnpm typecheckpnpm lintpnpm test— 402 unit + 124 Storybook browser tests passNew coverage:
test/channel-combobox.test.tsx(filtering, hash-insensitive matching, empty state, click and Enter commits, arrow navigation and wrapping, Escape-without-closing-the-dialog, selected marking) andtest/send-dialog-channel-picker.test.tsx(the picker inside a modal dialog, where the portalled popup is most likely to go wrong). The suites that drove the old<select>now drive the combobox via apickChannelhelper.Manually verified in Chromium against the built stylesheet, since jsdom can't see any of it: the dropdown escapes both dialogs' clipping panes, matches the field's width exactly (462px / 304px in the two layouts), caps at 240px and scrolls, and flips above the field on a 420px-tall viewport while staying inside it.
Notes for reviewer
The
# namespelling is applied everywhere the package names a channel, not just in the picker — leaving the update dialog on#generalwhile the send dialog said# generalwould read as a bug. Happy to scope it back to the picker alone if you'd rather.Generated by Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.