feat(fuselage): add onDismiss prop to Chip rendering a dedicated dismiss IconButton - #2145
Open
jeanfbrito wants to merge 1 commit into
Open
feat(fuselage): add onDismiss prop to Chip rendering a dedicated dismiss IconButton#2145jeanfbrito wants to merge 1 commit into
onDismiss prop to Chip rendering a dedicated dismiss IconButton#2145jeanfbrito wants to merge 1 commit into
Conversation
…iss IconButton Chip's dismiss action used to be triggered by clicking anywhere on the chip (the whole root was a <button>). The new opt-in onDismiss prop renders a non-interactive <span> root with an internal IconButton (cross, mini) as the only dismiss trigger, improving click precision and accessibility. The legacy whole-chip dismiss path is preserved byte-for-byte and documented as deprecated. Internal consumers (AutoComplete, MultiSelect/SelectedOptions, PaginatedMultiSelect) are migrated to onDismiss. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: ab6d7fc The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
dougfabris
requested changes
Aug 6, 2026
| onMouseDown, | ||
| }: SelectedOptionsProps) { | ||
| return ( | ||
| <Chip tabIndex={tabIndex} onDismiss={onMouseDown}> |
Member
There was a problem hiding this comment.
only the IconButton should be focusable
| } | ||
| {...(spanRest as unknown as Record<string, unknown>)} | ||
| > | ||
| <Margins all='x4'> |
Member
There was a problem hiding this comment.
looking at the rendered element it looks odd. Maybe we can add a gab in the style of the Chip, instead of keeping this Margin
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.
Proposed changes (including videos or screenshots)
Problem
Chip's dismiss action is currently triggered by clicking anywhere on the chip — the whole root element is a<button>, and the ✕ is just a decorative<Icon>. This makes the close affordance imprecise (any accidental click on the label or avatar removes the selection) and misleading for assistive technology, since the accessible name of the "close button" is the entire chip label.Solution
This PR introduces an opt-in
onDismissprop that moves the dismiss trigger to a dedicated, real button:onDismissprovided): the chip root becomes a non-interactive<span>and an internalIconButton(icon='cross', sizemini) is rendered as the only dismiss trigger. The button:dismissLabelprop (default:"Dismiss");preventDefault()onmousedownso it never steals focus from multiselect/autocomplete anchor inputs;disabled.onClick/onMouseDown, when passed alongsideonDismiss, behave as ordinary handlers on the chip body — they no longer mean "dismiss".onDismiss): rendering is byte-for-byte identical to the current release — root<button class="rcx-box rcx-chip">, whole-chip click dismisses, decorative cross icon. This behavior is now documented as deprecated (prose in the component JSDoc and an@deprecatedtag onrenderDismissSymbolonly, so the component itself is not flagged as deprecated at call sites).Backward compatibility
This is intentionally a non-breaking
minorrelease (changeset included). We audited all 11 directChipusages in the main Rocket.Chat repository before deciding on this design:onClickas the removal/selection handler → the legacy path keeps that contract untouched.<button class="rcx-chip">as the chip root → legacy DOM is unchanged.screen.getByRole('button')on legacy chips keep passing.form,type,name), so the type surface remains compatible.Internal consumers migrated
AutoComplete,MultiSelect/SelectedOptions, andPaginatedMultiSelectnow useonDismiss, so every product built on these pickers gets the improved dismiss behavior automatically by upgrading fuselage — no code changes required downstream.Notes on the migration:
AutoComplete: removal no longer relies onevent.currentTarget.value(which would resolve to the innerIconButtonafter the change); aremoveValue(value)helper is used instead. The publiconRemovecontract ofrenderSelectedis untouched.SelectedOptions: keeps its external prop surface (onMouseDown) and maps it internally toonDismiss, soMultiSelectneeded no changes.PaginatedMultiSelect: keptrole='option'/tabIndex={-1}on the chips — they implement the ARIA listbox pattern (parentrole='listbox') and are unrelated to dismiss focusability.Storybook
New/updated stories cover every combination:
Default,With Thumb Url,Legacy Dismissible(deprecated),Dismissible,Custom Dismiss Label,Disabled(both modes side by side), andWith Thumb. Docs and argTypes describe the new props and flag the deprecated ones.How it was verified
Chip,AutoComplete,MultiSelect,Options), plus 11/11 onPaginatedSelect/PaginatedMultiSelect. NewChipspecs assert: body click does not dismiss, IconButton click dismisses exactly once, accessible name (default and custom),disabledblocks the button, andonClickcoexists withonDismisswithout triggering dismissal.eslint(0 errors),stylelint,prettier,tsc --noEmit, and thed.tsbuild (tsc -p tsconfig.build.json --emitDeclarationOnly) all clean.dismiss— trigger confined to the button.MultiSelect, removing a selected option via its ✕ removes only that option and the anchor keeps focus (no dropdown flicker), confirming themousedown-preventDefaultguard works.Why a
spanroot instead of nesting buttonsA
<button>inside a<button>is invalid HTML and unreliably handled by browsers, so the dismissible variant's root must stop being a button. That structural change is exactly what's gated behind the opt-inonDismissprop to keep existing consumers safe.🤖 Generated with Claude Code